Vector 容器中的 C++ 智能指针
C++ Smart Pointers in a Vector container
我写了经典的 Shape Polymorphism 代码,但有点不同,因为我使用的是 Vector 容器和智能指针。
我不是C++专家,我想了解以下内容:
- 是否有内存泄漏?
- 如果我不调用
shapes.clear()
,是否会发生内存泄漏?
- 有没有更好的方法来使用智能指针和容器?
代码:
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
class Shape {
public:
virtual float getArea() = 0;
virtual ~Shape() {}
};
class Rectangle : public Shape {
public:
Rectangle(float w, float h) : width(w), height(h) {}
float getArea() {
return width * height;
}
private:
float width;
float height;
};
class Circle : public Shape {
public:
Circle(float r) : radius(r) {}
float getArea() {
return 3.141592653589793238462643383279502884 * radius * radius;
}
private:
float radius;
};
void printShapeAreas(vector<shared_ptr<Shape>> &shapes) {
for(int i = 0; i < shapes.size(); i++) {
cout << shapes[i]->getArea() << endl;
}
}
int main(int argc, char** argv) {
vector<shared_ptr<Shape>> shapes;
//this works, but you told me that is better to use make_shared
//=============================================================
//shapes.push_back(shared_ptr<Shape>(new Rectangle(10, 2)));
//shapes.push_back(shared_ptr<Shape>(new Rectangle(10, 3)));
//shapes.push_back(shared_ptr<Shape>(new Circle(2)));
//shapes.push_back(shared_ptr<Shape>(new Circle(3)));
//better
//======
shapes.push_back(std::make_shared<Rectangle>(10, 2));
shapes.push_back(std::make_shared<Rectangle>(10, 3));
shapes.push_back(std::make_shared<Circle>(2));
shapes.push_back(std::make_shared<Circle>(3));
printShapeAreas(shapes);
shapes.clear();//If I don't call shapes.clear(), is there going to be a memory leak?
return 0;
}
谢谢:)
- 没有
- 没有
- 也许吧。取决于您的需要。你所做的对于你粘贴的程序来说是合理的。
您的代码不包含内存泄漏。所以你的第一点很好。
不,如果您不调用 shapes.clear()
,就不会发生内存泄漏,因为 std::vector
的析构函数会清理容器。
我不知道关于在容器中使用共享指针的具体规则,所以我将跳过你的第三个问题。
但是,我可以提供创建 std::shared_ptr
s 的改进:
当您使用其构造函数创建共享指针时,即 shared_ptr<T>(new T());
,控制块 或保存有关该资源的簿记信息的结构,是从它指向的对象。这可能会导致很多缓存未命中。但是,如果您使用 std::make_shared
创建一个 shared_ptr
,控制块将分配给您想要的对象,因此,通过将它们放在一起,您至少可以减轻缓存未命中的成本:std::make_shared<T>();
。例如:std::make_shared<Circle>(3)
等同于写std::shared_ptr<Shape>(new Circle(3))
,但通常更好。
不会,不会有内存泄漏,这是(部分)智能指针的全部要点。当 vector
超出范围时,它会对其所有元素调用析构函数。在这种情况下,调用 shared_ptr<Shape>
上的析构函数将导致其内部共享引用计数达到 0
,从而销毁 Shape
对象。
- 没有,您发布的代码中没有 - 即使没有虚拟析构函数,
std::shared_ptr
也会正确销毁元素。 (不过你应该添加它 - 迟早会有人遇到这个问题,因为 std::unique_ptr
不会处理这种情况)
- 不会有任何内存泄漏,即使你不调用
.clear()
- std::vector
的析构函数会销毁所有元素,共享指针会销毁拥有的元素,如果它们是唯一的所有者。
- 如果不需要共享所有权,可以使用
std::unique_ptr
,但前提是 Shape
中有虚拟析构函数。您还应该尽可能使用 std::make_shared
和 std::make_unique
(C++14) 来避免序列点和抛出构造函数的棘手问题:本质上 f(std::unique_ptr<ConstructorAlwaysThrows>(new ConstructorAlwaysThrows()), std::unique_ptr<Foo>(new Foo()));
是否会泄漏内存,取决于编译器如何排序。
1) 我在这里没有看到内存泄漏。然而迟早会有一个:你的形状析构函数不是虚拟的。这意味着形状总是使用基本析构函数而不是正确的析构函数来销毁。 IE。如果你的派生形状之一有一天会分配内存,那么它是必须释放它的析构函数,它不会被调用。
2) 什么都不会发生:如果你不做 clear()
,当剩下 main()
时形状无论如何都会被破坏,导致它的竞争被破坏。
3) 也许吧,但是这个已经很不错了。
Is there any memory leak?
不,你在这里调用了shared_ptr
的析构函数。
If I don't call shapes.clear(), is there going to be a memory leak?
这取决于,如果你的 vector
活着那么它的所有内容都会活着,如果 vector
死了那么它会自动杀死它的内容。
Is there a better way to use smart pointers and containers?
make_shared
可用于避免 new
抛出异常时的问题,方法是确保 shared_ptr
调用其析构函数来删除内存。然而,这将需要从 shared_ptr<Rectangle>
到 shared_ptr<Shape>
的转换,如果烦人的话这是自动的(它涉及复制 shared_ptr
并删除原始文件)。
我写了经典的 Shape Polymorphism 代码,但有点不同,因为我使用的是 Vector 容器和智能指针。
我不是C++专家,我想了解以下内容:
- 是否有内存泄漏?
- 如果我不调用
shapes.clear()
,是否会发生内存泄漏? - 有没有更好的方法来使用智能指针和容器?
代码:
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
class Shape {
public:
virtual float getArea() = 0;
virtual ~Shape() {}
};
class Rectangle : public Shape {
public:
Rectangle(float w, float h) : width(w), height(h) {}
float getArea() {
return width * height;
}
private:
float width;
float height;
};
class Circle : public Shape {
public:
Circle(float r) : radius(r) {}
float getArea() {
return 3.141592653589793238462643383279502884 * radius * radius;
}
private:
float radius;
};
void printShapeAreas(vector<shared_ptr<Shape>> &shapes) {
for(int i = 0; i < shapes.size(); i++) {
cout << shapes[i]->getArea() << endl;
}
}
int main(int argc, char** argv) {
vector<shared_ptr<Shape>> shapes;
//this works, but you told me that is better to use make_shared
//=============================================================
//shapes.push_back(shared_ptr<Shape>(new Rectangle(10, 2)));
//shapes.push_back(shared_ptr<Shape>(new Rectangle(10, 3)));
//shapes.push_back(shared_ptr<Shape>(new Circle(2)));
//shapes.push_back(shared_ptr<Shape>(new Circle(3)));
//better
//======
shapes.push_back(std::make_shared<Rectangle>(10, 2));
shapes.push_back(std::make_shared<Rectangle>(10, 3));
shapes.push_back(std::make_shared<Circle>(2));
shapes.push_back(std::make_shared<Circle>(3));
printShapeAreas(shapes);
shapes.clear();//If I don't call shapes.clear(), is there going to be a memory leak?
return 0;
}
谢谢:)
- 没有
- 没有
- 也许吧。取决于您的需要。你所做的对于你粘贴的程序来说是合理的。
您的代码不包含内存泄漏。所以你的第一点很好。
不,如果您不调用 shapes.clear()
,就不会发生内存泄漏,因为 std::vector
的析构函数会清理容器。
我不知道关于在容器中使用共享指针的具体规则,所以我将跳过你的第三个问题。
但是,我可以提供创建 std::shared_ptr
s 的改进:
当您使用其构造函数创建共享指针时,即 shared_ptr<T>(new T());
,控制块 或保存有关该资源的簿记信息的结构,是从它指向的对象。这可能会导致很多缓存未命中。但是,如果您使用 std::make_shared
创建一个 shared_ptr
,控制块将分配给您想要的对象,因此,通过将它们放在一起,您至少可以减轻缓存未命中的成本:std::make_shared<T>();
。例如:std::make_shared<Circle>(3)
等同于写std::shared_ptr<Shape>(new Circle(3))
,但通常更好。
不会,不会有内存泄漏,这是(部分)智能指针的全部要点。当 vector
超出范围时,它会对其所有元素调用析构函数。在这种情况下,调用 shared_ptr<Shape>
上的析构函数将导致其内部共享引用计数达到 0
,从而销毁 Shape
对象。
- 没有,您发布的代码中没有 - 即使没有虚拟析构函数,
std::shared_ptr
也会正确销毁元素。 (不过你应该添加它 - 迟早会有人遇到这个问题,因为std::unique_ptr
不会处理这种情况) - 不会有任何内存泄漏,即使你不调用
.clear()
-std::vector
的析构函数会销毁所有元素,共享指针会销毁拥有的元素,如果它们是唯一的所有者。 - 如果不需要共享所有权,可以使用
std::unique_ptr
,但前提是Shape
中有虚拟析构函数。您还应该尽可能使用std::make_shared
和std::make_unique
(C++14) 来避免序列点和抛出构造函数的棘手问题:本质上f(std::unique_ptr<ConstructorAlwaysThrows>(new ConstructorAlwaysThrows()), std::unique_ptr<Foo>(new Foo()));
是否会泄漏内存,取决于编译器如何排序。
1) 我在这里没有看到内存泄漏。然而迟早会有一个:你的形状析构函数不是虚拟的。这意味着形状总是使用基本析构函数而不是正确的析构函数来销毁。 IE。如果你的派生形状之一有一天会分配内存,那么它是必须释放它的析构函数,它不会被调用。
2) 什么都不会发生:如果你不做 clear()
,当剩下 main()
时形状无论如何都会被破坏,导致它的竞争被破坏。
3) 也许吧,但是这个已经很不错了。
Is there any memory leak?
不,你在这里调用了shared_ptr
的析构函数。
If I don't call shapes.clear(), is there going to be a memory leak?
这取决于,如果你的 vector
活着那么它的所有内容都会活着,如果 vector
死了那么它会自动杀死它的内容。
Is there a better way to use smart pointers and containers?
make_shared
可用于避免 new
抛出异常时的问题,方法是确保 shared_ptr
调用其析构函数来删除内存。然而,这将需要从 shared_ptr<Rectangle>
到 shared_ptr<Shape>
的转换,如果烦人的话这是自动的(它涉及复制 shared_ptr
并删除原始文件)。