8

我的一种方法中有以下代码:

vector<Base*> units;
Base *a = new A();
Base *b = new B();
units.push_back(a);
units.push_back(b);

我应该在退出方法之前销毁 a 和 b 指针吗?或者我应该以某种方式破坏指针的单位向量?

编辑1:

这是另一个有趣的案例:

vector<Base*> units;
A a;
B b;        
units.push_back(&a);
units.push_back(&b);

这个案子怎么办?现在我不必使用删除或智能指针。

谢谢

4

10 回答 10

10

如果退出该方法,units将自动销毁。但不是ab。那些你需要明确销毁的。

或者,std::shared_ptr如果你有 C++11,你可以使用它来为你做这件事。

std::vector<std::shared_ptr<Base>> units;

而且您几乎像以前一样使用向量,但不必担心函数存在时的内存泄漏。我说差不多了,因为你需要使用std::make_shared分配到向量中。

于 2013-05-29T07:55:37.113 回答
9

一个相当老式的解决方案,适用于所有编译器:

for ( vector<Base*>::iterator i = units.begin(); i != units.end(); ++i )
    delete *i;

在 C++11 中,这变得很简单:

for ( auto p : units )
    delete p;

您的第二个示例不需要指针释放;实际上,这样做将是一个严重的错误。但是,它确实需要小心确保ab至少保持有效units。出于这个原因,我建议不要使用这种方法。

于 2013-05-29T07:57:33.293 回答
4

您需要遍历向量及其delete包含的每个指针。删除向量会导致内存泄漏,因为它的元素指向的对象不会被删除。

TL;DR:对象仍然存在,指针丢失 == 内存泄漏。

于 2013-05-29T07:55:38.903 回答
3

是的,您应该销毁这些指针(假设您没有在其他地方返回向量)。

您可以使用 std::for_each 轻松完成,如下所示:

std::for_each( units.begin(), units.end(), []( Base* p ) { delete p; } );
于 2013-05-29T07:56:24.350 回答
2

如果这两种情况匹配,则不应删除。

  1. 创建的向量返回到函数的外侧。
  2. 在函数外部创建的向量,并假设可以从其他函数访问。

在其他情况下,您应该删除向量中指针指向的内存。否则在删除指针后,无法引用此内存位置并调用内存泄漏。

vector<Base*>::iterator it;

for ( it = units.begin(); it != units.end(); ){
      delete * it;         
}
于 2013-05-29T08:04:04.447 回答
2

我建议您在向量中使用 SmartPointers。使用智能指针比使用原始指针更好。如果您没有 C++11,您应该使用 std::unique_ptr、std::shared_ptr 或 std::weak_ptr 智能指针或 boost 等价物。这是这些智能指针的 boost 库文档。

在这个问题的上下文中,是的,您必须删除添加到向量中的指针。否则会导致内存泄漏。

于 2013-05-29T08:08:02.450 回答
1

是和不是。您不需要在函数删除它们,但出于您可能想象的其他原因。

您实际上是将对象的所有权授予向量,但向量并不知道这一点,因此不会自动在指针上调用 delete。因此,如果您将拥有的原始指针存储在向量中,则必须在一段时间内手动对它们调用 delete。但是

  1. 如果将向量从函数中取出,则不应破坏函数内部的对象,否则充满指向已释放内存的指针的向量将毫无用处,所以不。但在这种情况下,您应该确保在函数外部使用向量后销毁对象。
  2. 如果你不给函数外的向量,你应该销毁函数内部的对象,但是没有必要在自由存储区分配它们,所以不要使用指针和 new。您只需将对象本身推入/放置到向量中,然后它会负责销毁,因此您不需要删除。

除此之外:不要使用普通的 new。使用智能指针。无论您对它们做什么,智能指针都会妥善销毁所包含的对象。无需使用new,无需使用delete。曾经。(除非您正在编写自己的低级数据结构,例如智能指针)。所以如果你想要一个充满拥有指针的向量,这些应该是智能指针。这样您就不必担心是否、何时以及如何销毁对象并释放内存。

于 2013-05-29T07:59:12.360 回答
1

你必须删除它们,除非你有内存泄漏,如果我在下面的代码中注释了析构函数从未调用的两个删除行,你还必须将基类的析构函数声明为虚拟。正如其他人提到的,最好使用智能指针。

#include <iostream>
#include <vector>

class Base
{
public:
  virtual ~Base(){std::cout << "Base destructor" << std::endl;};
};

class Derived : public Base
{
  ~Derived(){std::cout << "Derived destructor" << std::endl;};
};

int main()
{
  std::vector<Base*> v;
  Base *p=new Base();
  Base *p2=new Derived();
  v.push_back(p);
  v.push_back(p2);

  delete v.at(0);
  delete v.at(1);
};

输出:

Base destructor
Derived destructor
Base destructor

带有非虚拟基析构函数的输出(内存泄漏):

Base destructor
Base destructor
于 2013-05-29T08:00:34.617 回答
1

将指针存储在向量中的最佳方法是使用 smart_ptr 而不是原始指针。一旦调用向量 DTOR 并且控制退出 DTOR,所有 smart_ptrs 将被引用计数。而且你永远不应该为 smart_ptrs 的内存泄漏而烦恼。

于 2013-05-29T09:13:41.447 回答
1

在第一个示例中,您最终将不得不删除aand ,但在超出范围b时不一定要删除。通常你会在超出范围units之前这样做,但这不是唯一可能的情况。units这取决于目的是什么。
可能(稍后在同一个函数中)别名aor b,或两者兼而有之,因为您希望它们比units函数范围更有效。您可以同时将它们放入两个unit对象中。或者,许多其他可能的事情。

重要的是销毁向量(在这种情况下在作用域结束时自动)会销毁向量持有的元素,仅此而已。元素是指针,销毁指针没有任何作用。如果您还想销毁指针指向的内容(以不泄漏内存),则必须手动执行此操作(for_each使用 lambda 即可)。
如果您不想明确地完成这项工作,智能指针可以为您自动完成。

第二个示例(在 Edit1 下)不需要您删除任何内容(事实上这甚至是不可能的,您可能会看到尝试这样做的崩溃),但这种方法可能是有害的。

只要您在units之后ab离开范围内不再引用任何内容,该代码就会运行良好。不幸的是,如果你这样做。

从技术上讲,这样的事情甚至可能在不可见的情况下发生,因为units在 之后被销毁a,但幸运的是,~vector它不会取消引用指针元素。它只是销毁它们,对于指针没有任何作用(微不足道的析构函数)。
但是想象一下某人是如此“聪明”以至于扩展了向量类,或者也许你在未来的某一天应用这种模式(因为它“工作正常”)到另一个这样做的对象。砰,你死定了。你甚至不知道它是从哪里来的。

我真正不喜欢代码的地方,即使它是严格“合法的”,它可能会导致崩溃或表现出损坏的、不可重现的行为的情况。但是,它不会立即崩溃。“损坏”的代码应该立即崩溃,因此您会发现有问题,并且您被迫修复它。不幸的是,这里不是这种情况。

似乎会起作用,可能会持续数年,直到有一天它不起作用。最终你会忘记这一点,ab住在当前的堆栈帧上,并从其他位置引用向量中不存在的对象。也许您在代码的未来版本中动态分配向量,因为您将它传递给另一个函数。也许它会继续看起来有效。
然后,您将花费数小时(可能还有其他人的时间)试图找出为什么一段不可能失败的代码会产生错误的结果或崩溃。

于 2013-05-29T10:41:16.770 回答