C++ 允许删除吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3150942/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is delete this allowed?
提问by Martijn Courteaux
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 new
ly-created.
delete this;
如果删除语句是将在该类的该实例上执行的最后一条语句,是否允许?当然,我确信由this
-pointer表示的对象是new
ly-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?
我可以这样做吗?
回答by JaredPar
The C++ FAQ Lite has a entry specifically for this
C++ FAQ Lite 有一个专门针对此的条目
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).
只要你小心,一个对象自杀是可以的(删除这个)。
回答by Jerry Coffin
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 destrucors does mention delete this
in one place (§[class.dtor]/13):
是的,delete this;
已经定义了结果,只要(如您所指出的)您确保对象是动态分配的,并且(当然)在对象被销毁后永远不要尝试使用该对象。多年来,很多问题都被问到标准具体说了什么delete this;
,而不是删除其他一些指针。对此的回答相当简短:它什么也没说。它只是说 thatdelete
的操作数必须是一个表达式,它指定一个指向对象或对象数组的指针。它详细介绍了一些事情,例如它如何确定调用什么(如果有)释放内存的函数来释放内存,但是关于delete
(§[expr.delete])的整个部分根本没有delete this;
具体提及。关于 destructor 的部分确实提到delete this
在一处(§[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).
在虚拟析构函数的定义点(包括隐式定义(15.8)),非数组释放函数被确定为好像表达式 delete this 出现在析构函数类的非虚拟析构函数中(见 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.
这往往支持标准认为delete this;
有效的想法——如果它无效,它的类型就没有意义。delete this;
据我所知,这是标准提到的唯一地方。
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.
无论如何,有些人认为delete this
这是一种令人讨厌的黑客行为,并告诉任何愿意倾听的人应该避免这种行为。一个经常提到的问题是难以确保类的对象只能动态分配。其他人认为这是一个完全合理的习语,并一直使用它。就我个人而言,我介于两者之间:我很少使用它,但是当它似乎是适合该工作的工具时,请毫不犹豫地使用它。
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 start to you 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'ttie it to any lexical scope in the code, or anything on that order.
您第一次使用这种技术是处理一个几乎完全属于它自己的生命的对象。James Kanze 引用的一个例子是他为一家电话公司工作的计费/跟踪系统。当你开始打电话时,有些东西会注意到这一点并创建一个phone_call
对象。从那时起,该phone_call
对象将处理电话呼叫的详细信息(在您拨号时建立连接,向数据库添加一个条目以说明呼叫何时开始,如果您进行电话会议,可能会连接更多人等)最后一个人挂断电话,phone_call
对象做最后的记账(例如,在数据库中添加一个条目来说明你什么时候挂断电话,这样他们就可以计算你的电话持续了多长时间),然后销毁自己。的一生phone_call
对象是基于第一个人何时开始呼叫以及最后一个人何时离开呼叫——从系统其余部分的角度来看,它基本上是完全任意的,因此您不能将其绑定到代码中的任何词法范围,或该订单上的任何内容。
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.
对于可能关心这种编码的可靠性的任何人来说:如果您拨打欧洲几乎任何地方或从欧洲任何地方拨打电话,则很有可能(至少部分)由代码处理正是这样做的。
回答by Mark Ransom
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.
delete this
不过,我认为这是惯用的 C++,我只是将其作为一种好奇心呈现出来。
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.
注意:如果您使用的编译器早于 C++11,您可以使用std::auto_ptr
代替std::unique_ptr
,它会做同样的事情。
回答by Bob Bryan
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.
设计 C++ 的原因之一是使代码重用变得容易。通常,应该编写 C++ 以便无论类是在堆上、数组中还是在堆栈上实例化,它都可以工作。“删除这个”是一种非常糟糕的编码实践,因为它只有在堆上定义了单个实例时才有效;并且最好不要有另一个 delete 语句,大多数开发人员通常使用它来清理堆。这样做还假设将来没有维护程序员会通过添加删除语句来解决错误感知的内存泄漏。
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.
即使您事先知道您当前的计划是只在堆上分配一个实例,但如果将来某个幸运的开发人员出现并决定在堆栈上创建一个实例怎么办?或者,如果他将类的某些部分剪切并粘贴到他打算在堆栈上使用的新类中会怎样?当代码到达“delete this”时,它将关闭并删除它,但是当对象超出范围时,它将调用析构函数。然后析构函数将尝试再次删除它,然后你就被灌输了。在过去,做这样的事情不仅会搞砸程序,还会导致操作系统和计算机需要重新启动。在任何情况下,都强烈不建议这样做,并且几乎总是应避免这样做。我将不得不绝望,认真地涂抹,
回答by Kirill V. Lyadvinsky
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; }
.
这是允许的(只是在那之后不要使用该对象),但我不会在实践中编写这样的代码。我认为,delete this
应该调用只出现在功能release
或Release
看起来像:void release() { ref--; if (ref<1) delete this; }
。
回答by UnknownGosu
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:
好吧,在组件对象模型 (COM) 中,delete this
构造可以是Release
方法的一部分,只要您想释放获取的对象,就会调用该方法:
void IMyInterface::Release()
{
--instanceCount;
if(instanceCount == 0)
delete this;
}
回答by Zack Yezek
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.
当最后一个引用离开堆栈或被删除时,引用计数将变为零。然后,您对象的默认行为将调用“删除此”以进行垃圾收集——我编写的库在基类中提供了一个受保护的虚拟“CountIsZero”调用,以便您可以为缓存等内容覆盖此行为。
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.
使这个安全的关键是不允许用户访问有问题的对象的构造函数(使其受保护),而是让他们调用一些静态成员——工厂——如“静态引用 CreateT(...)”。这样你就肯定知道它们总是用普通的“new”构建的,并且没有原始指针可用,所以“delete this”永远不会爆炸。
回答by Edward Strange
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?
当然,您使用的是 RAII 对象,因此您实际上根本不需要调用 delete ......对吗?
回答by Krazy Glew
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.
这是一个古老的问题,但@Alexandre 问道“为什么会有人想要这样做?”,我想我可以提供一个我今天下午正在考虑的示例用法。
Legacy code. Uses naked pointers Obj*obj with a delete obj at the end.
遗留代码。使用裸指针 Obj*obj 在末尾带有删除 obj。
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 lotsof 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.
我正在考虑使它成为一个引用计数的智能指针。但是如果我要在任何地方使用,就会有很多代码需要更改ref_cnt_ptr<Obj>
。如果你混合裸 Obj* 和 ref_cnt_ptr,你可以在最后一个 ref_cnt_ptr 消失时隐式删除对象,即使有 Obj* 仍然活着。
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.
所以我正在考虑创建一个explicit_delete_ref_cnt_ptr。即引用计数指针,其中删除仅在显式删除例程中完成。在现有代码知道对象生命周期的一个地方使用它,以及在我的新代码中使用它使对象保持更长时间的存活时间。
Incrementing and decrementing the reference count as explicit_delete_ref_cnt_ptr get manipulated.
随着explicit_delete_ref_cnt_ptr 被操纵,递增和递减引用计数。
But NOT freeing when the reference count is seen to be zero in the explicit_delete_ref_cnt_ptr destructor.
但是当在explicit_delete_ref_cnt_ptr 析构函数中引用计数为零时不释放。
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.
好的,类似的东西。引用计数指针类型不会自动删除 rc'ed ptr 析构函数中指向的对象,这有点不寻常。但似乎这可能会使混合裸指针和 rc'ed 指针更安全一些。
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.
但是我突然想到:如果指向的对象,被指点者,知道它正在被引用计数,例如,如果计数在对象内部(或在其他一些表中),那么例程 delete_if_rc0 可能是指针对象,而不是(智能)指针。
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.)
(顺便说一句,我知道代码不太正确 - 如果我添加所有细节,它的可读性就会降低,所以我就这样离开了。)
回答by Swift - Friday Pie
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
只要对象在堆中,删除这是合法的。您需要要求对象仅为堆。唯一的方法是使析构函数受保护——这样删除只能从类中调用,因此您需要一种方法来确保删除