C++ 为什么使用指针向量被认为是不好的?

C++ 为什么使用指针向量被认为是不好的?,c++,pointers,vector,std,C++,Pointers,Vector,Std,最近我遇到了一个观点,我不应该使用指针向量。 我想知道——为什么我不能 例如,如果我有一个类foo,可以这样做: vector <foo*> v; v.push_back(new foo()); 向量v; v、 向后推(新的foo()); 我已经看到一些人反对这种做法,为什么 我将专门讨论一个指针向量,它负责管理指向对象的生命周期,因为这是唯一一个指针向量显然是有问题的选择的情况 还有更好的选择。具体而言: std::vector<std::shared_ptr<fo

最近我遇到了一个观点,我不应该使用指针向量。 我想知道——为什么我不能

例如,如果我有一个类
foo
,可以这样做:

vector <foo*> v;
v.push_back(new foo());
向量v; v、 向后推(新的foo());
我已经看到一些人反对这种做法,为什么

我将专门讨论一个指针向量,它负责管理指向对象的生命周期,因为这是唯一一个指针向量显然是有问题的选择的情况

还有更好的选择。具体而言:

std::vector<std::shared_ptr<foo>> v;
std::vector v;

std::vector v;

boost::ptr_向量v;//www.boost.org
上面的版本告诉用户如何处理对象的生存期。相反,使用原始指针可能会导致指针被删除一次或多次,尤其是当代码随着时间的推移而修改时,或者当涉及异常时

如果您使用“共享”或“唯一”等界面,则会自行记录用户的生命周期管理。当您使用原始指针时,您必须清楚地记录您如何处理对象的生命周期管理,并希望正确的人在正确的时间阅读文档


使用原始指针的向量的好处是,您可以更灵活地处理生命周期管理,并且可以消除一些性能和空间开销。

因为向量的析构函数不会对指针调用
delete
,所以很容易意外泄漏内存。向量的析构函数调用向量中所有元素的析构函数,但原始指针没有析构函数

但是,您可以使用智能指针向量来确保销毁向量将释放其中的对象
vector
可以在C++11中使用,在C++98和TR1中可以使用
vector
(尽管
shared_ptr
与原始指针或
unique_ptr
相比有轻微的开销)


Boost还有一个特殊的功能,即销毁时特殊删除行为内置于容器本身,因此您不需要智能指针。

其中一个问题是异常安全

例如,假设在某个地方抛出异常:在本例中,调用
std::vector
的析构函数。但是这个析构函数调用不会删除向量中存储的原始指针。因此,这些指针管理的资源会泄漏(它们可能是内存资源,因此存在内存泄漏,但也可能是非内存资源,例如套接字、OpenGL纹理等)

相反,如果您有一个智能指针向量(例如,
std::vector
),那么如果调用该向量的析构函数,则正确删除向量中的每个指向项(由智能指针安全拥有),并调用其析构函数。因此,与每个项目相关联的资源(向量中指向的“smartly”)被正确释放


请注意,观察原始指针的向量很好(假设观察项的生存期超过向量的生存期)。问题在于原始拥有指针。

在容器中存储普通指针可能导致内存泄漏和指针悬空。在容器中存储指针并不定义指针的任何类型的所有权。因此,容器不知道构造和复制操作的语义。当从容器中删除元素时,容器不知道如何正确销毁它们,当执行复制操作时,不知道所有权语义。当然,你可以自己处理这些事情,但是仍然有可能出现人为错误

使用智能指针将所有权和销毁语义留给它们


另一件需要提及的事情是,容器被划分为多个部分——它们存储实际提供的对象,而不是副本,因此它实际上是指针的集合。非侵入式指针有一些优点,因此您不能概括为容器中的指针在任何时候都应该避免,但在大多数情况下还是建议使用。使用原始指针向量并不是必要的坏样式,只要您记住指针没有所有权语义。当你开始使用
new
delete
时,通常意味着你做错了什么

<>特别是在现代C++代码中,使用<代码>新<代码> >或<代码>删除>代码>时,是构造UNIQUQU-PTR的,或者用自定义删除器构造SyrdYPPTR。

例如,假设我们有一个类实现了一个双向
,一个
包含一些
顶点

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);
}
类顶点
{
公众:
顶点();
//原始指针。没有所有权
向量边;
}
类图
{
公众:
图(){};
void addNode()
{
Vertex.push_back(new Vertex);//在C++14中:preferred std::make_unique
}
//未显示:我们的Graph类实现了一个遍历其节点的方法
私人:
//唯一\u ptr.明确所有权
向量顶点;
}
空心连接(顶点*a,顶点*b)
{
a->边缘。推回(b);
b->边缘。向后推(a);
}
注意我在那个
顶点
类中有一个原始
顶点
*的向量吗?我可以这样做,因为它所指向的
顶点的生存期是由类
管理的。我的
Vertex
类的所有权仅通过查看代码就可以明确

一个不同的答案建议使用共享的ptr。我个人不喜欢这种方法,因为一般来说,共享指针使得很难对对象的生命周期进行推理。在这个特殊的例子中
boost::ptr_vector<foo> v; // www.boost.org
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);
}