指向向量的指针 vs 指针向量 vs 指向指针向量的指针

13

想知道在 C++ 中关于向量的最佳实践是什么。

如果我有一个包含矢量成员变量的类。那么这个向量应该何时声明为:

  1. 包含值的“整个对象”向量成员变量,即 vector<MyClass> my_vector;
  2. 指向向量的指针,即 vector<MyClass>* my_vector;
  3. 指向指针的向量,即 vector<MyClass*> my_vector;
  4. 指向指针向量的指针,即 vector<MyClass*>* my_vector;

我有一个具体的例子,在我的一个类中,我当前将向量声明为情况 4,即 vector<AnotherClass*>* my_vector;,其中 AnotherClass 是我创建的另一个类之一。

然后,在构造函数的初始化列表中,我使用 new 创建了这个向量:

MyClass::MyClass()
: my_vector(new vector<AnotherClass*>())
{}

在我的析构函数中,我执行以下操作:

MyClass::~MyClass()
{
  for (int i=my_vector->size(); i>0; i--)
  {
    delete my_vector->at(i-1);
  }
  delete my_vector;
}

我的类的某个方法中,向量的元素被添加。我无法提前知道有多少对象将被添加到我的向量中。这是在代码执行时基于解析 xml 文件时决定的。

这种做法好吗?或者应该将向量声明为其他情况之一:1、2 或 3?

什么情况下使用哪种情况?

我知道如果向量元素是另一个类的子类(多态),则应该使用指针作为向量元素。但是,在其他情况下应该使用指针吗?

非常感谢!!

6个回答

4
通常情况下,解决方案1是您想要的,因为它是C++中最简单的:您不必担心管理内存,C++会为您完成所有这些工作(例如,您就不需要提供任何析构函数)。
有特定情况下,这种方法不起作用(尤其是在使用多态对象时),但总体而言,这是唯一好的方式。
即使在使用多态对象或需要堆分配对象(出于任何原因)时,裸指针也几乎从来不是一个好主意。相反,请使用智能指针或智能指针容器。现代C++编译器提供了即将到来的C++标准的shared_ptr。如果您正在使用尚未具备此功能的编译器,则可以使用Boost实现

3
我想知道为什么人们在这种情况下从不提及 Boost.Pointer ContainerBoost.Reference - Björn Pollex
@Space 我有一些了解:“使用智能指针或智能指针容器。” 但我从未使用过它们,因此我不太自信谈论它们的好处。 - Konrad Rudolph
@Konrad:“……在容器中使用原始指针几乎从来都不是一个好主意。”只是好奇——除了在容器中使用原始指针的(手动)内存管理方面外,还有什么其他原因不能在容器中使用原始指针吗?我依稀记得有一个类似于这样的原因:“随着容器(比如vector)的增长 / 收缩,它们可能会分配 / 释放 / 复制 / 移动它们的内容,如果它们的内容是指向对象的原始指针而不是 shared_ptr,则可能会引起问题 / 不良副作用。这是准确 / 正确的吗? - decimus phostle
@decimus 这难道不足以成为一个理由吗?我认为你提到的理由并不完全准确。指针本身可以被复制和移动而不会出现任何问题。只有当对象需要管理这些指针时才会变得棘手,因为这时你需要提供适当的复制和赋值语义。 - Konrad Rudolph
@KonradRudolph 对不起,我以为你在谈论这个 vector<MyClass>* my_vector;,但我刚刚注意到你是在谈论传统的那个,没有任何指针的东西。我的错。 - Everyone
显示剩余2条评论

1

绝对是第一!

您使用向量进行自动内存管理。使用指向向量的原始指针意味着您不再获得自动内存管理,这是没有意义的。

至于值类型:所有容器基本上都假定具有值类似的语义。同样,当使用指针时,您必须进行内存管理,而向量的目的就是为您完成此操作。这也在书籍C++编码标准的第79项中进行了描述。如果需要使用共享所有权或“弱”链接,请改用适当的智能指针。


0

手动删除向量中的所有元素是一种反模式,并违反了C++中的RAII习惯用法。因此,如果您必须在vector中存储对象的指针,则最好使用“智能指针”(例如boost::shared_ptr)来方便资源销毁。例如,boost::shared_ptr在销毁对象的最后一个引用时自动调用delete

另外,没有必要使用new分配MyClass::my_vector。一个简单的解决方案是:

class MyClass {

   std::vector<whatever> m_vector;
};

假设whatever是一个智能指针类型,那么就不需要额外的工作。这就是全部,当MyClass实例的生命周期结束时,所有资源都会自动销毁。
在许多情况下,您甚至可以使用一个简单的std::vector<MyClass> - 当向量中的对象可以安全复制时,就可以这样做。

当你说“手动删除向量中的所有元素”时,你是指不是在析构函数中吗?根据维基百科链接的解释:“资源获取即初始化(RAII)是指资源在对象创建时被获取,并在相同对象的销毁时被释放,即使发生错误也能保证执行。”这是否意味着如果你在析构函数中删除向量中的元素,就不会违反RAII原则? - tir38

0
在你的例子中,向量是在对象创建时创建的,并且在对象销毁时被销毁。这正是当将vector作为类的普通成员时所得到的行为。
此外,在你目前的方法中,当复制对象时会遇到问题。默认情况下,指针会导致平面复制,这意味着所有对象的副本都共享同一个向量。这就是为什么,如果你手动管理资源,通常需要大三件套的原因。
指针的向量在多态对象的情况下很有用,但你应该考虑其他选择:
  1. 如果向量拥有对象(这意味着它们的生命周期受向量的限制),您可以使用 boost::ptr_vector
  2. 如果对象不是由向量拥有,您可以使用 boost::shared_ptr 的向量,或者使用 boost::ref 的向量。

-1

指向 vector 的指针很少有用 - 构造和析构 vector 的成本很低。

对于 vector 中的元素,没有正确答案。 vector 有多经常更改?复制构造 vector 中的元素的成本有多高?其他容器是否有引用或指向 vector 元素的指针?

作为经验法则,我会选择不使用指针,直到您“看到”或“测量”您的类的复制成本很高。当然,您提到的情况,即在 vector 中存储基类的各种子类,将需要使用指针。

如果您的设计否则需要您使用指针作为 vector 元素,则像 boost::shared_ptr 这样的引用计数智能指针可能是最佳选择。


所以在我的例子中,如果_AnotherClass_是一个有很多成员变量的大类,将这些元素存储为向量中的指针可能是一个好主意?以避免复制大对象。 - Lisa
1
@Lisa:没错。正确的答案只能是“这要看情况”。 - Erik
你能详细说明第一点吗 - “指向向量的指针很少有用 - 向量的构造和析构很便宜。” 为什么? - dmonopoly

-1

复杂的答案:这取决于情况。

如果您的向量是共享的或者其生命周期与嵌入它的类不同,那么最好将其保留为指针。 如果您引用的对象没有(或具有昂贵的)复制构造函数,则最好保留指针的向量。相反,如果您的对象使用浅拷贝,则使用对象的向量可以防止泄漏...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接