C++ C+中的递归析构函数+;

C++ C+中的递归析构函数+;,c++,memory-leaks,destructor,C++,Memory Leaks,Destructor,假设我有一个类似于以下内容的类: #include <vector> class element { public: element(); ~element(); virtual void my_func(); private: std::vector<element*> _elements; }; 另外,如果我这样做(也就是不要使用new): 从析构函数调用delete this(这就是destroy()所做的)总是错误的,因为析构函数是

假设我有一个类似于以下内容的类:

#include <vector>

class element
{
public:
   element();
   ~element();

   virtual void my_func();

private:
   std::vector<element*> _elements;
};
另外,如果我这样做(也就是不要使用
new
):

从析构函数调用
delete this
(这就是
destroy()
所做的)总是错误的,因为析构函数是在删除当前对象时调用的


这将是一个问题,因为
e1_1
将在调用其析构函数时自动销毁。由于这会调用
destroy()
,在未使用
new
分配的对象上调用
delete this
,因此会导致未定义的行为。(如果幸运的话,它会立刻炸到你的脸上。)

最好从析构函数中删除
删除此
,只让析构函数删除对象向量中的对象:

for(std::vector<element*>::size_type i = 0; i < _elements.size(); ++i)
  delete _elements[i];
检查指针的
NULL
ness是不必要的,因为
delete
被定义为在传递给它的指针为
NULL
时不执行任何操作

element::~element()
{
    typedef std::vector<element*>::const_iterator iterator;
    for (iterator it(_elements.begin()); it != _elements.end(); ++it)
        delete *it;
}
元素
被销毁时,其
\u元素
容器将自动销毁。当容器被销毁时,它将销毁其每个元素。
std::unique_ptr
拥有它指向的对象,当
std::unique_ptr
被销毁时,它将销毁它指向的元素

通过在这里使用
std::vector
,您不需要提供自己的析构函数,因为所有这些内置功能都会为您进行清理

如果您希望能够复制
元素
树,您仍然需要提供自己的复制构造函数和复制分配操作符来克隆树。但是,如果不需要树是可复制的,则不需要像自己管理内存那样声明复制操作:
std::unique_ptr
容器本身是不可复制的,因此它作为成员变量的存在将抑制隐式生成的复制操作

  • 删除此
    不应该在那里,因为根据定义,该对象已经处于销毁状态

  • 如果复制
    元素
    ,则其成员
    向量
    中的所有指针也将被复制;然后,当原始指针超出范围时,它们的指针将被销毁,并且
    元素
    副本有一个悬挂指针的
    向量
    。您需要一个复制运算符和赋值运算符

  • 因此,仅仅这个基本的开始就已经造成了两个非常严重的错误。这是一个要避免设计的迹象。对于使用它的程序员来说,所有权语义似乎并不十分清楚

    为什么这里需要动态分配?为什么不存储
    元素
    s对象本身

    #include <vector>
    
    class element
    {
    public:
       void add_element(element const& e) {
          _elements.push_back(e);
       }
    private:
       std::vector<element> _elements;
    };
    
    #包括
    类元素
    {
    公众:
    无效添加元素(元素常量和e){
    _元素。推回(e);
    }
    私人:
    std::矢量元素;
    };
    

    如果您以前多次引用同一
    元素
    进入其他不同的
    元素
    ,这将微妙地改变您的程序,但您必须自己决定是否需要这种混乱的依赖关系。

    不,这样做行不通。您永远不会删除此。你就是不知道。基本上,去掉销毁方法,将所有内容放入
    ~element()
    中,除了
    删除此部分。而不是调用destroy方法,只需
    delete_元素[i]

    您可以调用
    delete\u元素。在(i)
    中,您不应该测试
    在析构函数中该
    是否为空。它不能为空。调用
    是否会删除\u元素。at(i)
    是否会正确处理所有子元素?换句话说,它们是否会按正确的顺序(从下到上)进行处理?因为我认为如果我删除一个
    元素
    ,它的
    \u元素
    ,那将是一个漏洞
    调用
    某物的类的析构函数,然后释放指针。应尽可能避免使用原始指针。在这种情况下:确保您确实需要一个指针向量(通常仅当
    元素
    是抽象基类或不可复制时才需要),如果是这样,请选择
    ptr_向量
    @Philipp:在这种情况下,需要一个指针容器:不能用不完整的类型实例化标准库容器,因此,
    元素
    不能将
    std::vector
    作为成员。(或者,你可以使用第三方容器库。Boost有一个容器库,它的实例化要求不那么严格。)那么,在这种情况下,你能把
    delete*it
    看作是对
    ~元素
    的递归调用吗?@Eric:实际上,删除一个对象会首先调用对象的析构函数(它应该清理它所负责的一切),然后释放对象占用的内存。最后一段是误导性的;
    if
    从来都不是必需的,而不是整个片段(当然,作为相关说明,
    删除这个
    很少有用!)@Lightness:我正在改进它。
    :)
    感谢您的评论--我之所以需要使用指针,是因为
    元素
    是一个具有
    虚拟
    函数的基类。这是确保执行正确形式的虚拟函数的唯一方法。@Eric:OK;那可能是有道理的。不过,还是考虑一个包装的指针实现,比如<代码> STD::SysDypPTR < /C> >代码>删除此;code>是有效的,偶尔也有用。是的,但你仍然从来没有做过,就像你从来没有做过某些其他事情一样,你有时会因为方便而最终做这些事情。
    class element
    {
    public:
       element();
       ~element();
    
    private:
       std::vector<element*> _elements;
    };
    
    element::~element()
    {
        destroy();
    }
    
    for(int i = 0; i < _elements.size(); ++i)
       _elements.at(i)->destroy();
    
    element el_1;
    
    for(std::vector<element*>::size_type i = 0; i < _elements.size(); ++i)
      delete _elements[i];
    
    if(this != NULL) 
       delete this;
    
    element::~element()
    {
        typedef std::vector<element*>::const_iterator iterator;
        for (iterator it(_elements.begin()); it != _elements.end(); ++it)
            delete *it;
    }
    
    std::vector<std::unique_ptr<element>> _elements;
    
    #include <vector>
    
    class element
    {
    public:
       void add_element(element const& e) {
          _elements.push_back(e);
       }
    private:
       std::vector<element> _elements;
    };