代码之家  ›  专栏  ›  技术社区  ›  Martijn Courteaux

允许删除吗?

  •  209
  • Martijn Courteaux  · 技术社区  · 15 年前

    允许这样做吗 delete this; 如果delete语句是将在该类实例上执行的最后一条语句?当然,我确信 this 指针是 new 创造了。

    我在想这样的事情:

    void SomeModule::doStuff()
    {
        // in the controller, "this" object of SomeModule is the "current module"
        // now, if I want to switch over to a new Module, eg:
    
        controller->setWorkingModule(new OtherModule());
    
        // since the new "OtherModule" object will take the lead, 
        // I want to get rid of this "SomeModule" object:
    
        delete this;
    }
    

    我可以这样做吗?

    10 回复  |  直到 6 年前
        1
  •  217
  •   Maciej Szpakowski    10 年前

    C++ FAQLite有一个专门针对这个的条目

    我认为这句话概括得很好

    只要你小心,对象自杀是可以的(删除这个)。

        2
  •  75
  •   Jerry Coffin    6 年前

    对, delete this; 已经定义了结果,只要(如您所指出的)您确保对象是动态分配的,并且(当然)永远不会在对象被销毁后尝试使用它。多年来,人们对该标准具体说明了什么提出了许多问题。 删除此; ,而不是删除其他指针。答案相当简短:什么都不说。它只是说 delete 的操作数必须是指定对象指针或对象数组的表达式。它非常详细地介绍了一些事情,比如它如何确定调用什么(如果有的话)释放内存的释放函数,但是关于 删除 (§[expr.delete])没有提到 删除此; 具体来说。关于destructor的章节确实提到 delete this 在一个地方(§[class.dtor]/13):

    在虚拟析构函数(包括隐式定义(15.8))的定义点,非数组释放函数被确定为表达式删除出现在析构函数类的非虚拟析构函数中的该函数(见8.3.5)。

    这倾向于支持标准所考虑的观点 删除此; 为了有效——如果它无效,它的类型就没有意义。这是标准中唯一提到的地方 删除此; 据我所知。

    不管怎样,有些人认为 删除此 一个讨厌的黑客,告诉任何愿意听的人应该避免。一个常见的问题是,很难确保类中的对象只能动态分配。其他人认为它是一个非常合理的习语,并一直使用它。就我个人而言,我处于中间位置:我很少使用它,但当它似乎是工作的正确工具时,不要犹豫。

    你使用这种技术的主要时间是和一个几乎完全拥有自己生命的物体在一起。JamesKanze引用的一个例子是他为一家电话公司工作的计费/跟踪系统。当你开始打电话时,一些东西会注意到这一点,并创建一个 phone_call 对象。从那时起, 电话呼叫 对象处理电话呼叫的详细信息(拨号时进行连接,在数据库中添加一个条目以说明呼叫何时开始,如果进行会议呼叫,则可能连接更多人等),当最后一个呼叫人挂断时,将 电话呼叫 对象执行其最后的簿记(例如,在数据库中添加一个条目来说明挂断的时间,这样他们可以计算调用的时间),然后销毁自己。的生命 电话呼叫 对象是基于第一个人何时开始调用和最后一个人何时离开调用——从系统的其他部分来看,它基本上是完全任意的,所以 不能 将它绑定到代码中的任何词汇范围,或者任何按此顺序排列的内容。

    对于任何一个关心这种编码有多可靠的人来说:如果你在欧洲的任何地方打电话,或者从欧洲的任何地方打电话,很有可能它正是由这样的代码处理的(至少部分)。

        3
  •  44
  •   Mark Ransom    10 年前

    如果它吓到了你,就有一个完全合法的黑客:

    void myclass::delete_me()
    {
        std::unique_ptr<myclass> bye_bye(this);
    }
    

    我想 delete this 虽然是惯用的C++,但我只是把它作为一个好奇心。

    在这种情况下,这个构造实际上是有用的——您可以在抛出需要对象成员数据的异常之后删除该对象。该对象在引发之前保持有效。

    void myclass::throw_error()
    {
        std::unique_ptr<myclass> bye_bye(this);
        throw std::runtime_exception(this->error_msg);
    }
    

    注意:如果使用比C++ 11大的编译器,则可以使用 std::auto_ptr 而不是 std::unique_ptr 它也会做同样的事情。

        4
  •  22
  •   Bob Bryan    13 年前

    C++被设计的原因之一是使代码易于重用。一般来说,C++应该被编写,以使该类在堆、数组或堆栈上实例化。删除这个“是一个非常糟糕的编码实践,因为只有在堆上定义了一个实例时,它才会起作用;最好不要有另一个delete语句,这通常是大多数开发人员用来清理堆的。这样做还假设将来没有维护程序员会通过添加一条DELETE语句来修复错误感知的内存泄漏。

    即使您事先知道您当前的计划是只在堆上分配一个实例,如果将来某个幸运的开发人员来了,并决定在堆栈上创建一个实例呢?或者,如果他将类的某些部分剪切并粘贴到一个他打算在堆栈上使用的新类,该怎么办?当代码达到“删除这个”时,它将关闭并删除它,但当对象超出范围时,它将调用析构函数。然后,析构函数将再次尝试删除它,然后您将被托管。在过去,这样做不仅会破坏程序,而且需要重新启动操作系统和计算机。在任何情况下,强烈建议不要这样做,而且几乎应始终避免这样做。我必须绝望,认真地粉饰,或者真的恨我为之工作的公司写这样的代码。

        5
  •  19
  •   Kirill V. Lyadvinsky    15 年前

    这是允许的(之后不要使用对象),但我不会在实践中编写这样的代码。我认为 delete this 只应出现在调用的函数中 release Release 看起来像: void release() { ref--; if (ref<1) delete this; } .

        6
  •  13
  •   UnknownGosu    15 年前

    嗯,在组件对象模型(COM)中 delete this 建筑可以是 Release 每当要释放Aquisited对象时调用的方法:

    void IMyInterface::Release()
    {
        --instanceCount;
        if(instanceCount == 0)
            delete this;
    }
    
        7
  •  7
  •   Edward Strange    15 年前

    你可以这样做。但是,您不能分配给它。因此,你说这样做的原因,“我想改变看法”,似乎很可疑。在我看来,更好的方法是让持有视图的对象替换该视图。

    当然,你使用的是raii对象,所以你根本不需要调用delete…对吧?

        8
  •  6
  •   Zack Yezek    11 年前

    这是引用计数对象的核心习语。

    引用计数是确定性垃圾收集的一种强大形式——它确保对象管理自己的生命周期,而不是依赖“智能”指针等来为其执行此操作。底层对象只能通过“引用”智能指针访问,其设计使指针在实际对象中递增和递减成员整数(引用计数)。

    当最后一个引用从堆栈中除去或被删除时,引用计数将变为零。然后,对象的默认行为将是调用“delete this”进行垃圾收集-我编写的库在基类中提供了一个受保护的虚拟“countiszero”调用,以便您可以为缓存等操作重写此行为。

    使此安全的关键是不允许用户访问有问题的对象的构造函数(使其受保护),而是让他们调用一些静态成员-工厂-如“static reference createt(…)”。这样,您就可以确定它们总是用普通的“new”构建的,并且没有原始指针可用,所以“delete this”永远不会爆炸。

        9
  •  3
  •   Bo Persson Touseef    13 年前

    这是一个古老的,有人回答的问题,但是@Alexandre问:“为什么会有人想这样做?”我想我可以提供一个我今天下午考虑的示例用法。

    遗留代码。使用裸指针obj*obj,并在末尾使用delete obj。

    不幸的是,我有时需要,而不是经常,让物体活得更长。

    我正在考虑让它成为一个参考计数的智能指针。但是会有 太多了 如果我要使用 ref_cnt_ptr<Obj> 到处都是。如果混合使用裸obj*和ref cnt_ptr,则可以在最后一个ref_cnt_ptr消失时隐式删除对象,即使obj*仍然存在。

    所以我正在考虑创建一个显式的删除指针。即引用计数指针,其中删除仅在显式删除例程中完成。在现有代码知道对象生存期的地方使用它,在我的新代码中使用它可以使对象保持更长的生存期。

    增加和减少引用计数,作为显式的“删除”参数。

    但如果在显式“删除”析构函数中的引用计数为零,则不会释放。

    仅当在显式类似于删除的操作中,引用计数为零时才释放。例如:

    template<typename T> class explicit_delete_ref_cnt_ptr { 
     private: 
       T* ptr;
       int rc;
       ...
     public: 
       void delete_if_rc0() {
          if( this->ptr ) {
            this->rc--;
            if( this->rc == 0 ) {
               delete this->ptr;
            }
            this->ptr = 0;
          }
        }
     };
    

    好吧,差不多。引用计数指针类型不自动删除rc'ed ptr析构函数中指向的对象是有点不寻常的。但这似乎会使混合裸指针和RC'ED指针更加安全。

    但到目前为止还没有必要删除这个。

    但我突然想到:如果指向的对象指针知道它正在被引用计数,例如,如果计数在对象内部(或在其他表中),那么例程delete_if_r rc0可以是指针对象的方法,而不是(智能)指针。

    class Pointee { 
     private: 
       int rc;
       ...
     public: 
       void delete_if_rc0() {
            this->rc--;
            if( this->rc == 0 ) {
               delete this;
            }
          }
        }
     };
    

    实际上,它根本不需要是成员方法,但可以是自由函数:

    map<void*,int> keepalive_map;
    template<typename T>
    void delete_if_rc0(T*ptr) {
            void* tptr = (void*)ptr;
            if( keepalive_map[tptr] == 1 ) {
               delete ptr;
            }
    };
    

    (顺便说一句,我知道代码不太正确——如果我添加所有的细节,代码就变得不那么可读了,所以我就这样离开它。)

        10
  •  0
  •   Swift - Friday Pie    9 年前

    删除只要对象在堆中,这是合法的。 您需要只要求对象是堆。 唯一的方法是使析构函数受到保护-这样,只能从类中调用delete,因此需要一个确保删除的方法

    推荐文章