【问题标题】:How do I destruct a dynamically allocated array of dynamic object?如何销毁动态分配的动态对象数组?
【发布时间】:2021-12-16 05:17:32
【问题描述】:

我写了一个类vector,它的成员是一个动态分配的数组

template <typename T> struct vector{
    T* elem;int capacity;
    /* 
     *capacity is the size of array, not number of elements in the array .
     *the default capacity is 3.
     */

    vector(int c=3,T e=0){ /* initializing all elements as e */
        T* elem=new T[capacity = c];
        for(int i=0;i<capacity;i++){
            elem[i]=e;
        }
    }
    ~vector(){
        delete[] elem;
    }
};

现在是重点,关于vector 的析构函数。如果成员elem中的元素也是动态分配的对象obj,并且这个对象也有自己的析构函数

int main(){
    vector<obj*> vec;
    vec.elem[0] = new obj(parameter)
    vec.elem[1] = new obj(parameter)
    vec.elem[2] = new obj(parameter)
    ...
}

deletevector的析构函数中的所有对象都需要吗?像这样

~vector(){
    for(int i=0;i<capacity;i++){
        delete elem[i];
    }
    delete[] elem;
}

或者我应该只有delete[] elemobj 的析构函数来完成剩下的工作?

【问题讨论】:

  • 只需在析构函数中放置一个断点或一些调试输出,然后观察会发生什么。在相关说明中,您的 C++ 教程是否包含有关 newdelete 配对的规则?
  • 使用vector&lt;std::unique_ptr&lt;obj&gt;&gt;
  • 如果您不能使用标准库(出于某种原因),请创建自己的 unique_ptr 与您的 vector 并排。你永远不应该拥有拥有原始指针的向量。
  • 简单规则:你new你也需要delete。除非有别的东西为你做。
  • 我是 C++ 的初学者。由于我加入的数据结构课程的要求,我编写了自己的向量。禁止使用 STL。我相信我应该配对 new 和 delete 是真的,这是错的吗?谢谢,我会尝试调试看看结果:D

标签: c++ destructor


【解决方案1】:

vector的析构函数中的所有对象都需要删除吗?像这样

技术上是的,但是如果您想要一个不代表所有权的指针向量怎么办?您可能很容易导致双重删除一个对象,或者尝试删除一个基于堆栈的对象:

obj obj_a;
obj* obj_b = new obj;

vector<obj*> obj_ptrs;
obj_ptrs.elem[0] = &obj_a;
obj_ptrs.elem[1] = &obj_a;
obj_ptrs.elem[2] = obj_b;

delete obj_b;

是否需要用vector删除指向的对象与vector无关

最干净的解决方法是使用std::unique_ptr,它是一种对象类型,它持有一个指针并在它被销毁时将其删除:

#include <memory>

template <typename T> struct vector {
  // ...
  ~vector() {
    // The vector is only responsible for deleting the array.
    delete[] elem;
  }
};
// ...


void foo() {
  vector<std::unique_ptr<obj>> obj_ptrs;

  obj_ptrs.elem[0] = std::make_unique<obj>();
  obj_ptrs.elem[1] = std::make_unique<obj>();
  obj_ptrs.elem[2] = std::make_unique<obj>();

  obj stack_obj;

  vector<obj*> obj_no_own_ptrs;
  obj_no_own_ptrs.elem[0] = obj_ptrs.elem[0].get();
  obj_no_own_ptrs.elem[1] = obj_ptrs.elem[0].get();
  obj_no_own_ptrs.elem[2] = &stack_obj;

  // Everything gets deleted
  // No double-delete concern
}

【讨论】:

  • 谢谢(虽然我不明白,因为我从未学过 STL 哈哈哈 :D)。我相信学习 C++ 的巨大挑战仍然存在。 (人生苦短,我想用Python QAQ)
  • @YoRHa 学习如何使用std::vectorstd::unique_ptr 比手工编写等价物花费的时间要少得多。只是说。
【解决方案2】:

一般来说,当你在一个对象中调用 delete 时,它首先调用类的析构函数,然后释放分配对象的内存。

所以,是的。如果您创建一个类调用向量,那么如果 elem 之前是用 new 动态分配的,则有必要调用 delete。

新建始终必须与删除配对。标准方法是,例如将 new 放在类的构造函数中,将 delete 放在类的析构函数中。

在您的情况下,在您的类中,您正在为其他类动态分配空间,这恰好也是一个动态分配的指针。

first_object->second_object->third_object...

在这种情况下,first_object 包含一个动态分配给 second_object 的向量。 second_object 当然可以包含更多动态分配的内存。

first_object 的析构函数中,删除second_object,因此调用第二个对象的析构函数并释放其内存。调用second_object 的析构函数应该删除第三个对象,所以它的内存也被释放了。

如果你分配了内存但没有释放它,你就会开始弄乱内存,因为你正在分割它。如果您在没有 new 的情况下调用 delete,或者在已删除的对象中调用,则会出现段错误。将新闻放在构造函数中并在析构函数中删除总是一个好主意。

现在您也可以使用shared_ptrunique_ptr。他们会在超出范围时自动删除其内容。

  1. 特别是shared_ptr 将在指向一个资源的最后一个指针超出范围时删除其内容

  2. unique_ptr 将在超出范围时删除其内容,因为根据定义,禁止多个指针指向其内容。

    {

    std::shared_ptr foo = std::make_shared();

    std::shared_ptr foo2(new Foo());

    }

两个共享指针在超出范围时会自动删除其内容。第一个选项是首选且更有效(make_shared 比使用 new 调用构造函数更好)。您也可以使用unique_ptr,其特征与 shared_ptr 相似,它们不允许多个指针指向同一资源。

我认为您可以阅读有关 智能指针 和有关 RAII 的信息,这将对您有所帮助

【讨论】:

  • shared_ptr 是“方便的”,因为它们是可复制的,但它们有一些严重的缺点。方便并不是使用它们的充分理由。仅在绝对必要时才应使用它们,
  • shared_ptr 绝对不比unique_ptr 高效。事实上,情况正好相反。例如,shared_ptr 使用 类型擦除 作为删除器,并且具有显着的运行时和内存开销。
  • 更不用说它们的原子性会干扰代码重新排序,从而阻止优化。
  • @DanielLangr 我想我解释得不好,我想说 std::make_shared 比调用构造函数更有效,我现在重写它,我的错误是英语不是我的母语
猜你喜欢
  • 2019-03-01
  • 2020-01-10
  • 1970-01-01
  • 1970-01-01
  • 2021-04-26
  • 2010-09-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多