c ++:如何为结合了指针的向量编写析构函数

c++: how to write a destructor for a vector combined with a pointer

我需要为学校的练习编写一个析构函数。我已经尝试为 class A 编写析构函数,这是正确的方法还是错误的方法?

Exercise on paper

练习说析构函数需要像这样开始:

A *pa = .......;
delete pa;

4 classes 的代码:

Class A
{
  private:
    vector <B*> b;
    vector <C*> c;
  public:
    ~A();
}

Class B
{
  private:
    vector <D*> d;
  public:
    ~B();
}

Class C
{
  private:
    vector <D*> d;
  public:
    ~C();
}

Class D
{
  private:
    vector <A*> a;
  public:
    ~D();
}

我已经尝试为 class A 编写析构函数, 这是正确的方法吗?

~A()
{
  for (int i = 0; i < b.size(); i++)
  {
     B* pa = b[i];
     delete pa;
  }

  for (int j = 0; j < c.size(); i++)
  {
    C* pa = c[i];
    delete pa;
  }

视情况而定。如果 A 拥有 向量中的指针,因此负责销毁指向的对象,并且对象是使用 new 创建的,那么是的,析构函数会似乎是正确的。

如果指向的对象不是用 new 创建的,或者指针不由 A 拥有,则 A 的析构函数不应删除它们。

如果 A 确实拥有指针,那将是糟糕的设计,而不是在 class A 中分配对象。但我假设,为了简单起见,该示例被排除在外。

作为旁注:如果 A 确实拥有指针并在析构函数中删除它们,那么您还应该实现复制构造函数和复制赋值运算符,以便它们进行深层复制或通过使class non-copyable。否则,如果您复制一个实例,您将以未定义的行为结束。

But as you see in Class B and Class C I go to class D, but there would be an error if I delete D?

这样做非常好,不会有错误,除非...

... 如果 A 拥有指向 BC 的指针,并且 BC 拥有指向 [=23= 的指针],而D拥有指向A的指针,所以它们都删除了指向的对象,则在class级别存在所有权循环。这仍然很好,除非...

...指针有一个循环。例如,D 的实例(我们称它为 dee)指向 A 的实例,后者指向 B 的实例,后者指向 dee ,然后你有一个循环,你最终删除了一个其析构函数已经启动的对象,这导致了未定义的行为。

所以,如果这样的指针循环是可能的,那么必须避免所有权循环。但是如果不能有指针循环,那么所有权循环可能存在。

假设你不确定classA中的两个vector中的指针都是不是NULL,那么您可以进行以下操作:

~A()
{
  for (int i = 0; i < b.size(); i++)
    delete b[i];
  b.clear(); // Making sure you do not access to deleted pointers

  for (int i = 0; i < c.size(); i++)
    delete c[i];
  c.clear();  // Making sure you do not access to deleted pointers
}

我们还清除了向量,以确保不会访问已删除的足尖点。或者,如果您仍然需要向量的大小,您可以设置 b[i] = nullptrc[i] = nullptr 在两个循环中,没有清除向量。

这取决于构造函数的作用。

笼统地说,构造函数建立一个class不变量(所有成员函数都可以假定为真的一组属性),所有在class外部使用的成员函数(例如 public 成员,为派生 classes 提供服务的受保护成员)保持不变。析构函数执行与构造函数相反的操作 - 例如,如果构造函数显式分配某些资源,析构函数将释放它。

如果该不变量包含 "all elements of b are assigned the result of operator new",那么析构函数必须使用 (相应的运算符 delete 释放这些元素,或者移交给其他执行此操作的对象。这通常被描述为所有权- class A 执行影响其他对象生命周期的操作,因此必须清理。

如果该不变量包含 "all elements of c are either the NULL pointer, or contain the address of an object supplied from elsewhere",则析构函数不应释放这些对象。例如,某些其他代码可能会提供指向 A 成员函数的指针,而该指针只是简单地放入 c 中。这假设您知道这些对象的生命周期是在您的 class A.

之外管理的

例如,考虑一个执行此操作的构造函数

 A::A(int num_b, const std::vector<C *> &in_c) : b(0), c(in_c)
 {
     b.reserve(num_b);    // so b doesn't keep on resizing itself below
     for (int i = 0; i < num_b; ++i)
        b.push_back(new B);
 }

其逻辑是 A 正在分配地址存储在 b 中的所有对象,但只是从 in_c 复制指针(可能在别处管理) .所以 class A 负责 b 中对象的生命周期,而不是 c.

中对象的生命周期

然后我们假设,如果成员函数更改 bc,那么在 A 的析构函数被调用时同样仍然适用 - b 中的所有对象都是由 class A 管理,但 c 中的则不是。 (换句话说,我们假设保持 class 不变量)。

那么析构函数可能看起来像

 A::~A()
 {
     for (std::vector<B *>::iterator i = b.begin(), end = b.end();
          i != end;
          ++i)
     {
          delete (*i);
     }
 }

请注意,我们不需要调整 bc 的大小,因为 std::vector 的析构函数会自行清理。但是,由于我们的 class 分配了对象并将它们的地址存储在 b 中 - std::vector 不知道 - 析构函数必须释放它们。

注意:以上示例适用于所有版本的 C++。 Post C++11,析构函数中的代码可以简化,例如使用新式循环或auto 类型推导。