【问题标题】:c++: how to write a destructor for a vector combined with a pointerc ++:如何为结合指针的向量编写析构函数
【发布时间】:2016-05-02 23:49:27
【问题描述】:

我需要为学校练习编写一个析构函数。我已经尝试为A类编写析构函数,这是对还是错?

Exercise on paper

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

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

4个类的代码:

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();
}

我已经尝试为 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;
  }

【问题讨论】:

  • 我可以在你的课堂上看到new,所以你不需要delete。最好使用智能指针。
  • 我假设这些指针是使用new分配的?
  • 你也没有constructor
  • 几个小时前你不是已经发布了这个确切的问题吗?请不要多次重新发布完全相同的问题,而是编辑现有问题。这也会将您的问题推到首页的问题列表中。
  • 删除未分配的指针会导致未知结果!

标签: c++ pointers vector destructor


【解决方案1】:

假设你不确定A类中的两个vectors中的指针都是不是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 在两个循环中,不清除向量。

【讨论】:

  • nullptr 在 c++11 中可用吗?
  • 向量在析构函数体结束后立即被销毁,因此除了防止以后在析构函数中意外访问之外,清除没有任何好处。正是出于这个原因,我清除了它们。
  • 谢谢你的回复我明白你的意思,但是B类和C类的情况怎么办?在这些的解构器中,我需要删除 A。那不会有问题吗?如果B已经删除了A,C就不能删除A吗?
  • @Michael 很公平,但在我看来,析构函数非常简单,以至于我一眼就能看出向量在其指针被删除后无法访问。任何设计良好的类的析构函数都是如此。我看到你删除了虚假陈述,所以我删除了我的反对票。
  • 是的,感谢您指出这一点,@user2079303。无论如何,删除后的访问严格依赖于未来的代码开发。所以,以防万一,通过这种方式,我确保不会发生任何不好的事情。无论如何,你可能是对的,因为这看起来是一个关于构造函数/析构函数的简单练习。
【解决方案2】:

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

一般来说,就是说构造函数建立一个类不变量(一组所有成员函数都可以假设为真的属性),所有在类外部使用的成员函数(例如公共成员,提供服务的受保护成员)到派生类)保持不变。析构函数的作用与构造函数的相反——例如,如果构造函数显式分配了一些资源,析构函数就会释放它。

如果该不变量包括“b 的所有元素都分配了运算符 new 的结果”,则析构函数必须使用(相应的运算符 delete 释放这些元素,或者移交给其他执行此操作的对象所以。这通常被描述为所有权 - 类 A 采取影响其他对象生命周期的操作,因此必须清理。

如果该不变量包括“c 的所有元素要么是 NULL 指针,要么包含从其他地方提供的对象的地址”,那么析构函数不应释放这些对象。例如,其他一些代码可能会提供一个指向A 的成员函数的指针,而该指针只是简单地放在c 中。这假设您知道这些对象的生命周期是在您的类之外管理的 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 复制指针(可能在其他地方管理)。所以A 类负责b 中对象的生命周期,但不负责c 中的对象的生命周期。

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

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

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

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

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

【讨论】:

    【解决方案3】:

    这取决于。如果A 拥有向量中的指针,因此负责销毁指向的对象并且对象是使用new 创建的,那么是的,析构函数似乎是正确的。

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

    如果A 确实拥有指针,那将是糟糕的设计,不要在A 类中分配对象。但我认为,为了简单起见,示例中没有提及。

    附带说明:如果 A 确实拥有指针并在析构函数中删除它们,那么您还应该实现复制构造函数和复制赋值运算符,以便它们进行深度复制或使类不可复制.否则,如果您复制实例,最终会出现未定义的行为。

    但是正如你在B类和C类中看到的那样,我去D类,但是如果我删除D会出现错误?

    这样做完全没问题,除非...

    ...如果A拥有指向BC的指针,并且BC拥有指向D的指针,并且D拥有指向A的指针,这样他们都删除了指向的对象,然后在类级别上有一个所有权周期。除非...

    ...有一个指针循环。例如,D 的一个实例(我们称它为dee)指向一个A 的实例,该实例指向一个B 的实例,该实例指向dee,那么你有一个循环,你结束了删除析构函数已经启动的对象,这会导致未定义的行为。

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

    【讨论】:

    • 是的,这只是正确思维方式的一个简单示例。但是正如你在B类和C类中看到的那样我去D类,但是如果我删除D会出现错误?
    • 谢谢你!我明白你的意思
    猜你喜欢
    • 1970-01-01
    • 2012-08-17
    • 1970-01-01
    • 2021-01-26
    • 1970-01-01
    • 2018-06-20
    • 2017-04-02
    • 2011-02-01
    • 1970-01-01
    相关资源
    最近更新 更多