最近我听到了一个观点,认为我不应该使用指针的向量。 我想知道 - 为什么我不能这样做?
例如,如果我有一个类foo
,可以这样做:
vector <foo*> v;
v.push_back(new foo());
我已经看到一些人对这种做法进行了负面评价,为什么会这样呢?
使用原始指针的向量并不一定是不好的风格,只要你记住这些指针没有所有权语义。当你开始使用 new
和 delete
时,通常意味着你正在做一些错误的事情。
特别地,在现代 C++ 代码中唯一应该使用 new
或 delete
的情况是构造 unique_ptr,或者使用自定义删除器构造 shared_ptr。
例如,假设我们有一个实现了双向 Graph
的类,一个 Graph
包含一些 Vertexes
。
class Vertex
{
public:
Vertex();
// raw pointer. No ownership
std::vector<Vertex *> edges;
}
class Graph
{
public:
Graph() {};
void addNode()
{
vertexes.push_back(new Vertex); // in C++14: prefer std::make_unique<>
}
// not shown: our Graph class implements a method to traverse over it's nodes
private:
// unique_ptr. Explicit ownership
std::vector<std::unique_ptr<Vertex>> vertexes;
}
void connect(Vertex *a, Vertex *b)
{
a->edges.push_back(b);
b->edges.push_back(a);
}
注意一下我在Vertex
类中有一个指向原始Vertex
的向量?这是因为它所指向的Vertexes
的生命周期由Graph
类管理。从代码上看,我的Vertex
类拥有权是明确的。
另一个回答建议使用shared_ptr
。我个人不喜欢这种方法,因为共享指针通常很难理解对象的生命周期。在这个特定的例子中,由于Vertexes
之间存在循环引用,共享指针根本无法工作。
在容器中存储裸指针可能导致内存泄漏和悬空指针。在容器中存储指针并不定义指针的所有权。因此,容器不知道销毁和复制操作的语义。当容器中的元素被移除时,容器不知道如何正确地销毁它们;当执行复制操作时,没有所有权的语义可用。当然,你总是可以自己处理这些事情,但仍然存在人为错误的可能性。
使用智能指针将所有权和销毁语义留给它们。
另一个需要提到的是,容器分为非侵入式和侵入式容器 - 它们存储实际提供的对象而不是副本,因此实际上是一组指针。非侵入式指针有一些优点,所以不能一概而论在所有情况下都应该避免在容器中使用指针,但在大多数情况下建议使用智能指针。
delete
,所以很容易意外泄漏内存。向量的析构函数会调用向量中所有元素的析构函数,但原始指针没有析构函数。vector<unique_ptr<foo>>
可在C++11中使用,在C++98中可以使用TR1中的vector<tr1::shared_ptr<foo>>
(尽管与原始指针或unique_ptr
相比,shared_ptr
具有轻微的开销)。其中一个问题是异常安全性(exception-safety)。
例如,假设某处抛出异常:在这种情况下,将调用std::vector
的析构函数。但此析构函数调用不会删除存储在向量中的原始所有指针。因此,由这些指针管理的资源将被泄漏(这些可以是内存资源,因此您将有一个内存泄漏,但它们也可以是非内存资源,例如套接字、OpenGL纹理等)。
相反,如果您有一个智能指针向量(例如std::vector<std::unique_ptr<Foo>>
),则如果调用向量的析构函数,则向量中每个智能指针安全拥有的指向物(由智能指针安全拥有)将被正确删除,从而调用其析构函数。因此,与每个项相关联的资源(在向量中以“聪明”的方式指向)将得到适当释放。
请注意,观察原始指针的向量很好(假设所观察项目的生命周期超过向量的生命周期)。问题在于原始所有指针。
std::vector<std::shared_ptr<foo>> v;
and
std::vector<std::unique_ptr<foo>> v;
boost::ptr_vector<foo> v; // www.boost.org
使用指针的向量没有任何问题。大多数人建议使用智能指针,但我必须说,使用指针的向量没有问题。我经常这样做。
我同意juanchopanza的观点,你示例中的指针来自new foo()。在一个正常的、完全有效的用例中,你可能会将对象放在另一个集合C中,这样当C被销毁时,对象将自动被销毁。然后,在对C中的对象进行深度操作的过程中,你可能会创建任意数量的其他包含指向C中对象的指针的集合。(如果其他集合使用对象副本,那么时间和内存会浪费,而引用集合则是明确禁止的。)在这种用例中,我们永远不希望在指针集合被销毁时销毁任何对象。
ptr_vector
或者vector<unique_ptr<>>
。 - Columbostd::vector
中有一个指针,并且您对其进行修改,则实际上并没有修改初始指针。 - nbro