【问题标题】:How do I *not* delete a member in a destructor?我如何*不*删除析构函数中的成员?
【发布时间】:2010-11-08 11:06:00
【问题描述】:

我希望我的类的析构函数删除整个对象,但其中一个成员除外,该成员在其他地方被删除。首先,这完全不合理吗?假设不是,我该怎么做?我认为创建一个空主体的析构函数会阻止所有成员被删除(因为析构函数不会做任何事情),但事实似乎并非如此。

【问题讨论】:

  • 一个简短的代码示例会很有用。

标签: c++ destructor ownership-semantics


【解决方案1】:

这是可能的,但基本上正如@dmckee 所说,这是一个所有权问题。如果是这种情况,您可以进行重新计数。即

class A
{

RefObj* obj;
A()
{

obj = new RefObj;

}

~A()
{
 obj->ReleaseRef();
}
}


RefObj
{

int m_iRefCounter;
RefObj()
{
m_iRefCounter = 1;
}
AddRef()
{
m_iRefCounter++;
}
ReleaseRef()
{
m_iRefCounter--
if(m_iRefCounter == 0)
{
 delete this;
}
}
}

}

【讨论】:

    【解决方案2】:

    这并非不合理,但应注意确保隐式处理任何托管资源的清理。

    (人们通常担心的第一个托管资源是内存,但任何可能泄漏的资源 - 内存、文件句柄、IDispatch 指针 - 都应该有隐式处理清理的代码)。

    对于由多个对象共享的托管资源(如果“这个对象”应该有一个指向被“那个对象”清理的东西的指针,几乎肯定是这种情况),您通常需要一个“引用计数指针”管理对象或“弱指针”,具体取决于您的生命周期要求。

    对于不共享的托管资源(尤其是那些在可能引发异常时需要正确管理的资源),那么 auto_ptr 或其他变体可能更合适。

    Scott Meyers Effective C++ 书籍是学习智能指针的一个合理起点,但在实践中,您可能应该只使用像 Boost 这样的经过审查的库,让其他人担心会遇到晦涩难懂的极端情况(比如会发生什么如果构造函数抛出异常?)对。

    【讨论】:

      【解决方案3】:

      首先,如果成员对象是按值包含的,那么当容器对象被销毁时,它就会超出范围,你无法阻止它被自动释放。

      如果相反,它被您的容器对象间接引用(例如使用指针),则您无需执行任何特别的操作来删除它。除非您明确编写代码,否则析构函数不会删除任何内容。

      至于这是否不合理的问题,我认为一般来说不是,但你必须明确(通常在文档中,因为 C++ 没有语言支持这个概念)拥有相关成员。

      【讨论】:

        【解决方案4】:

        为什么没有人提到弱指针和强指针?
        强指针是正常动作的智能指针。
        弱指针是一种智能指针,除非所有强指针都超出范围,否则无法删除自身。
        强指针表示所有权,弱指针表示共享。
        查看boost.shared_ptrboost.weak_ptrLoki's StrongPtr 的实现。
        也看看RAII。如果您知道 RAII,您自己就会知道这个问题的答案。

        【讨论】:

        • 没有人提到弱指针和强指针,因为这个问题是 C++ 的一个非常基础的水平。
        • 所以也许是他进步一点的时候了。
        【解决方案5】:

        当你谈到在析构函数中删除类成员时,你必须区分不是指针的成员和那些是指针的成员。假设您有这样的课程:

        
        class Foo
        {
        public:
          Foo() {p = new int;}
         ~Foo(){}
        
        private:
         int a;
         int *p;
        };
        

        这个类有 2 个数据成员:一个整数 a 和一个指向整数 p 的指针。当析构函数被调用时,对象被销毁,这意味着它的所有成员的析构函数都被调用。即使析构函数的主体为空,也会发生这种情况。对于原始类型,比如整数,调用它的析构函数只是意味着它占用的内存将被释放。但是,销毁指针时有一个问题:默认情况下,它指向的任何内容都不会被销毁。为此,您必须明确调用delete

        所以在我们的示例中,a 将在调用析构函数时被销毁,p 也将被销毁,但不会是 p 指向的任何内容。如果您希望释放p 指向的内存,Foo 的析构函数应如下所示:

        
        ~Foo() {delete p};
        

        所以,回到你的问题,当对象的析构函数被调用时,你的类中所有不是指针的成员都将被销毁。另一方面,如果您的成员是指针,则它们指向的任何内容都不会被销毁,除非您在析构函数中专门为它们调用 delete。

        【讨论】:

          【解决方案6】:

          如果您已为此成员动态分配内存,则可以在销毁对象之前共享对该成员的引用并且确保该成员未在对象的析构函数中销毁。不过我觉得这种做法不太合理。

          【讨论】:

            【解决方案7】:

            析构函数中的代码只是删除动态分配的成员。成员的销毁不是可选的,您只能控制之前显式分配的内容的释放(使用 operator new)。

            可以使用 shared_ptr 获得您想要做的事情,其中​​您的类和外部代码共享一个指向同一个外部对象的指针。这样,只有当所有指向该对象的指针超出范围时,它才会被删除。但要注意不要做循环引用,shared_ptr 没有“垃圾收集器”的智慧。

            当然,您可以使用这些位置共享的常规指针,但在大多数情况下,这是一个坏主意,很容易让您在以后对正确的资源释放感到头疼。

            【讨论】:

              【解决方案8】:

              首先,这完全是 不合理?

              我不会说不合理,也许是有问题的。

              一个类拥有它是完全有效的,因此应该注意清理,同时在另一个类中拥有指向该对象的引用或指针。

              但是,如果第二类真的应该有那个指针,这可能是有问题的,我宁愿总是在需要时使用 get 方法来检索该指针,例如通过调用父类或某些资源管理器。

              【讨论】:

                【解决方案9】:

                如果成员包含在 value 中(不是通过指针或引用),那么您无法阻止它被删除,并且您不应该这样做。

                如果你想在别处删除它,那么让它包含在指针或引用中。

                class House
                {
                  Door door; //contained by value, will be destroyed when the House is
                }
                
                class House
                {
                  Door& door; //contained by reference, will not be destroyed when the House is
                }
                

                【讨论】:

                • 但是如果我把你的房子锁起来,我希望门会被毁在瓦砾中。
                • 那是因为门通常包含在房屋内的价值中。如果不是“House”和“Door”而是“Order”和“Customer”,取消订单通常不会破坏相关客户。
                • @Martin York 是的,但你不要先去我的冰箱,在拐角处找到一张贴有烤鸡店地址的贴纸,然后也将其销毁,在销毁我的过程中房子。
                • @Daniel 烤鸡上的贴纸是一个“弱”参考:鸡知道商店但不拥有它。不过,烤鸡实际上可能有一个引用计数引用,因此当最后一个烤鸡被消耗时,商店会自动进行垃圾收集。
                【解决方案10】:

                我认为在大多数情况下,如果您不在同一个动作中破坏整个对象,您就是在自找麻烦。听起来您的类应该对该成员有一个清理方法,该方法在析构函数中调用。如果由于某种原因必须尽快销毁该成员,则该方法可以提前返回。

                【讨论】:

                  【解决方案11】:

                  取决于您所说的“已删除”是什么意思。如果它们不在智能指针中,并且没有被显式删除,那么它们不会被删除。只是类的一部分的成员:

                  class Bar {
                  //...
                  private: 
                    Foo foo;
                  };
                  

                  不会被析构函数删除(因为它们不是动态分配的),它们只是被销毁了。他们“生活”在课堂内,所以一旦被摧毁,它就消失了。

                  如果您正在查看两个位置之间的共享“所有权”,您需要的是动态分配的 shared_ptr:

                  #include <memory>
                  class Bar {
                  // ...
                  private:
                    std::tr1::shared_ptr<Foo> foo;
                  };
                  

                  【讨论】:

                    【解决方案12】:

                    简答:你不知​​道。

                    更长的答案:如果“成员”实际上是指向其他分配的指针,您可以安排不删除其他分配。

                    但通常,如果你在构造函数中分配了另一个块,你想在析构函数中删除它。其他任何事情都需要仔细处理相关块的“所有权”。这将很像普通 c 中的内存管理。可能,但充满危险。

                    祝你好运。

                    【讨论】:

                      猜你喜欢
                      • 2011-11-05
                      • 1970-01-01
                      • 2012-04-30
                      • 2017-02-10
                      • 2012-08-11
                      • 2016-01-29
                      • 2019-03-09
                      • 2017-10-03
                      • 2020-04-01
                      相关资源
                      最近更新 更多