是“删除这个”吗? C++ 允许吗?

发布于 2024-09-07 15:40:55 字数 501 浏览 9 评论 0原文

如果删除语句是将在该类的实例上执行的最后一条语句,是否允许删除此语句?当然,我确信 this 指针表示的对象是新创建的。

我在想这样的事情:

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

我可以这样做吗?

Is it allowed to delete this; if the delete-statement is the last statement that will be executed on that instance of the class? Of course I'm sure that the object represented by the this-pointer is newly-created.

I'm thinking about something like this:

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

Can I do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

北方的韩爷 2024-09-14 15:40:55

C++ FAQ Lite 有一个专门针对此问题的条目

我认为这句话很好地总结了这一点

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

The C++ FAQ Lite has a entry specifically for this

I think this quote sums it up nicely

As long as you're careful, it's OK for an object to commit suicide (delete this).

吃→可爱长大的 2024-09-14 15:40:55

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

在虚拟析构函数(包括隐式定义 (15.8))的定义点,非数组释放函数被确定为好像对于出现在非虚拟析构函数中的表达式delete this析构函数所属类的析构函数(参见 8.3.5)。

这往往支持这样的观点:标准认为 delete this; 是有效的——如果它无效,那么它的类型就没有意义。据我所知,这是标准唯一提到delete this;的地方。

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

第一次使用这种技术是在一个几乎完全拥有自己生命的对象上。 James Kanze 引用的一个例子是他为一家电话公司开发的计费/跟踪系统。当您开始拨打电话时,系统会记录下来并创建一个 phone_call 对象。从那时起,phone_call 对象会处理电话呼叫的详细信息(在拨号时建立连接、向数据库添加一个条目以说明呼叫何时开始,如果您这样做,可能会连接更多人)电话会议等)当通话中的最后一个人挂断电话时,phone_call 对象会进行最后的簿记(例如,向数据库添加一个条目来说明您何时挂断电话,因此他们可以计算出你的通话持续了多长时间),然后自行销毁。 phone_call 对象的生命周期基于第一个人开始通话的时间和最后一个人离开通话的时间 - 从系统其余部分的角度来看,它基本上完全是任意的,所以你无法将其与代码中的任何词法范围或该顺序上的任何内容联系起来。

对于任何可能关心这种编码的可靠性的人来说:如果您向欧洲、从欧洲或从欧洲的几乎任何地方拨打电话,那么很有可能它是由代码处理(至少部分)的。正是这样做的。

Yes, delete this; has defined results, as long as (as you've noted) you assure the object was allocated dynamically, and (of course) never attempt to use the object after it's destroyed. Over the years, many questions have been asked about what the standard says specifically about delete this;, as opposed to deleting some other pointer. The answer to that is fairly short and simple: it doesn't say much of anything. It just says that delete's operand must be an expression that designates a pointer to an object, or an array of objects. It goes into quite a bit of detail about things like how it figures out what (if any) deallocation function to call to release the memory, but the entire section on delete (§[expr.delete]) doesn't mention delete this; specifically at all. The section on destructors does mention delete this in one place (§[class.dtor]/13):

At the point of definition of a virtual destructor (including an implicit definition (15.8)), the non-array deallocation function is determined as if for the expression delete this appearing in a non-virtual destructor of the destructor’s class (see 8.3.5).

That tends to support the idea that the standard considers delete this; to be valid -- if it was invalid, its type wouldn't be meaningful. That's the only place the standard mentions delete this; at all, as far as I know.

Anyway, some consider delete this a nasty hack, and tell anybody who will listen that it should be avoided. One commonly cited problem is the difficulty of ensuring that objects of the class are only ever allocated dynamically. Others consider it a perfectly reasonable idiom, and use it all the time. Personally, I'm somewhere in the middle: I rarely use it, but don't hesitate to do so when it seems to be the right tool for the job.

The primary time you use this technique is with an object that has a life that's almost entirely its own. One example James Kanze has cited was a billing/tracking system he worked on for a phone company. When you start to make a phone call, something takes note of that and creates a phone_call object. From that point onward, the phone_call object handles the details of the phone call (making a connection when you dial, adding an entry to the database to say when the call started, possibly connect more people if you do a conference call, etc.) When the last people on the call hang up, the phone_call object does its final book-keeping (e.g., adds an entry to the database to say when you hung up, so they can compute how long your call was) and then destroys itself. The lifetime of the phone_call object is based on when the first person starts the call and when the last people leave the call -- from the viewpoint of the rest of the system, it's basically entirely arbitrary, so you can't tie it to any lexical scope in the code, or anything on that order.

For anybody who might care about how dependable this kind of coding can be: if you make a phone call to, from, or through almost any part of Europe, there's a pretty good chance that it's being handled (at least in part) by code that does exactly this.

你的心境我的脸 2024-09-14 15:40:55

如果它让你感到害怕,这里有一个完全合法的 hack:

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,它会执行相同的操作。

If it scares you, there's a perfectly legal hack:

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

I think delete this is idiomatic C++ though, and I only present this as a curiosity.

There is a case where this construct is actually useful - you can delete the object after throwing an exception that needs member data from the object. The object remains valid until after the throw takes place.

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

Note: if you're using a compiler older than C++11 you can use std::auto_ptr instead of std::unique_ptr, it will do the same thing.

挽心 2024-09-14 15:40:55

设计 C++ 的原因之一是为了方便重用代码。一般来说,C++ 应该被编写为无论类是在堆上、在数组中还是在堆栈上实例化,它都可以工作。 “删除这个”是一种非常糟糕的编码实践,因为它只有在堆上定义了单个实例时才有效;并且最好不要再有另一个删除语句,大多数开发人员通常使用该语句来清理堆。这样做还假设将来没有维护程序员会通过添加删除语句来解决错误感知的内存泄漏。

即使您事先知道当前的计划只是在堆上分配一个实例,但如果将来有一些无忧无虑的开发人员决定在堆栈上创建一个实例怎么办?或者,如果他将类的某些部分剪切并粘贴到他打算在堆栈上使用的新类,该怎么办?当代码到达“删除此”时,它将关闭并删除它,但是当对象超出范围时,它将调用析构函数。然后析构函数将尝试再次删除它,然后你就被淘汰了。在过去,这样做不仅会搞砸程序,而且操作系统和计算机都需要重新启动。无论如何,强烈不建议这样做,并且几乎应该始终避免这样做。我必须绝望、彻底崩溃,或者真的讨厌我工作的公司才能编写这样做的代码。

One of the reasons that C++ was designed was to make it easy to reuse code. In general, C++ should be written so that it works whether the class is instantiated on the heap, in an array, or on the stack. "Delete this" is a very bad coding practice because it will only work if a single instance is defined on the heap; and there had better not be another delete statement, which is typically used by most developers to clean up the heap. Doing this also assumes that no maintenance programmer in the future will cure a falsely perceived memory leak by adding a delete statement.

Even if you know in advance that your current plan is to only allocate a single instance on the heap, what if some happy-go-lucky developer comes along in the future and decides to create an instance on the stack? Or, what if he cuts and pastes certain portions of the class to a new class that he intends to use on the stack? When the code reaches "delete this" it will go off and delete it, but then when the object goes out of scope, it will call the destructor. The destructor will then try to delete it again and then you are hosed. In the past, doing something like this would screw up not only the program but the operating system and the computer would need to be rebooted. In any case, this is highly NOT recommended and should almost always be avoided. I would have to be desperate, seriously plastered, or really hate the company I worked for to write code that did this.

又爬满兰若 2024-09-14 15:40:55

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

It is allowed (just do not use the object after that), but I wouldn't write such code on practice. I think that delete this should appear only in functions that called release or Release and looks like: void release() { ref--; if (ref<1) delete this; }.

夜血缘 2024-09-14 15:40:55

那么,在组件对象模型 (COM) 中,delete this 构造可以是 Release 方法的一部分,只要您想要释放获取的对象,就会调用该方法:

void IMyInterface::Release()
{
    --instanceCount;
    if(instanceCount == 0)
        delete this;
}

Well, in Component Object Model (COM) delete this construction can be a part of Release method that is called whenever you want to release aquisited object:

void IMyInterface::Release()
{
    --instanceCount;
    if(instanceCount == 0)
        delete this;
}
岛歌少女 2024-09-14 15:40:55

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

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

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

确保安全的关键是不允许用户访问相关对象的构造函数(使其受到保护),而是让他们调用一些静态成员(FACTORY),例如“静态引用 CreateT(...)”。这样您就可以确定它们始终是用普通的“新”构建的,并且没有可用的原始指针,因此“删除它”永远不会爆炸。

This is the core idiom for reference-counted objects.

Reference-counting is a strong form of deterministic garbage collection- it ensures objects manage their OWN lifetime instead of relying on 'smart' pointers, etc. to do it for them. The underlying object is only ever accessed via "Reference" smart pointers, designed so that the pointers increment and decrement a member integer (the reference count) in the actual object.

When the last reference drops off the stack or is deleted, the reference count will go to zero. Your object's default behavior will then be a call to "delete this" to garbage collect- the libraries I write provide a protected virtual "CountIsZero" call in the base class so that you can override this behavior for things like caching.

The key to making this safe is not allowing users access to the CONSTRUCTOR of the object in question (make it protected), but instead making them call some static member- the FACTORY- like "static Reference CreateT(...)". That way you KNOW for sure that they're always built with ordinary "new" and that no raw pointer is ever available, so "delete this" won't ever blow up.

下雨或天晴 2024-09-14 15:40:55

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

当然,您正在使用 RAII 对象,因此您实际上根本不需要调用删除...对吗?

You can do so. However, you can't assign to this. Thus the reason you state for doing this, "I want to change the view," seems very questionable. The better method, in my opinion, would be for the object that holds the view to replace that view.

Of course, you're using RAII objects and so you don't actually need to call delete at all...right?

人│生佛魔见 2024-09-14 15:40:55

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

遗留代码。使用裸指针 Obj*obj 并在末尾添加删除 obj。

不幸的是,我有时需要(而不是经常)让对象保持更长时间的存活。

我正在考虑将其设为引用计数智能指针。但是,如果我要在任何地方使用 ref_cnt_ptr,就会有很多代码需要更改。如果你混合裸 Obj* 和 ref_cnt_ptr,当最后一个 ref_cnt_ptr 消失时,即使有 Obj* 仍然存在,你也可以隐式删除该对象。

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

当explicit_delete_ref_cnt_ptr被操作时增加和减少引用计数。

但当在explicit_delete_ref_cnt_ptr 析构函数中看到引用计数为零时,不会释放。

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

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_rc0可能是pointee 对象,而不是(智能)指针。

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

顺便说一句,我知道代码不太正确 - 如果我添加所有详细信息,它的可读性就会降低,所以我将其保留像这样。)

This is an old, answered, question, but @Alexandre asked "Why would anyone want to do this?", and I thought that I might provide an example usage that I am considering this afternoon.

Legacy code. Uses naked pointers Obj*obj with a delete obj at the end.

Unfortunately I need sometimes, not often, to keep the object alive longer.

I am considering making it a reference counted smart pointer. But there would be lots of code to change, if I was to use ref_cnt_ptr<Obj> everywhere. And if you mix naked Obj* and ref_cnt_ptr, you can get the object implicitly deleted when the last ref_cnt_ptr goes away, even though there are Obj* still alive.

So I am thinking about creating an explicit_delete_ref_cnt_ptr. I.e. a reference counted pointer where the delete is only done in an explicit delete routine. Using it in the one place where the existing code knows the lifetime of the object, as well as in my new code that keeps the object alive longer.

Incrementing and decrementing the reference count as explicit_delete_ref_cnt_ptr get manipulated.

But NOT freeing when the reference count is seen to be zero in the explicit_delete_ref_cnt_ptr destructor.

Only freeing when the reference count is seen to be zero in an explicit delete-like operation. E.g. in something like:

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

OK, something like that. It's a bit unusual to have a reference counted pointer type not automatically delete the object pointed to in the rc'ed ptr destructor. But it seems like this might make mixing naked pointers and rc'ed pointers a bit safer.

But so far no need for delete this.

But then it occurred to me: if the object pointed to, the pointee, knows that it is being reference counted, e.g. if the count is inside the object (or in some other table), then the routine delete_if_rc0 could be a method of the pointee object, not the (smart) pointer.

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

Actually, it doesn't need to be a member method at all, but could be a free function:

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

(BTW, I know the code is not quite right - it becomes less readable if I add all the details, so I am leaving it like this.)

溺ぐ爱和你が 2024-09-14 15:40:55

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

Delete this is legal as long as object is in heap.
You would need to require object to be heap only.
The only way to do that is to make the destructor protected - this way delete may be called ONLY from class , so you would need a method that would ensure deletion

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文