对象应该在 C++ 中删除自己吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/522637/
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
Should objects delete themselves in C++?
提问by Generic Error
I've spent the last 4 years in C# so I'm interested in current best practices and common design patterns in C++. Consider the following partial example:
过去 4 年我一直在使用 C#,所以我对 C++ 中当前的最佳实践和常见设计模式很感兴趣。考虑以下部分示例:
class World
{
public:
void Add(Object *object);
void Remove(Object *object);
void Update();
}
class Fire : Object
{
public:
virtual void Update()
{
if(age > burnTime)
{
world.Remove(this);
delete this;
}
}
}
Here we have a world responsible for managing a set of objects and updating them regularly. Fire is an an object that might be added to the world under many different circumstances but typically by another object already in the world. Fire is the only object that knows when it has burned out so currently I have it deleting itself. The object that created the fire is likely no longer in existence or relevant.
在这里,我们有一个世界负责管理一组对象并定期更新它们。火是一种可能在许多不同情况下添加到世界中的对象,但通常是由世界上已经存在的另一个对象添加的。火是唯一知道它何时烧毁的对象,所以目前我让它自行删除。引起火灾的物体可能不再存在或不相关。
Is this a sensible thing to do or is there a better design that would help clean up these objects?
这是明智的做法还是有更好的设计来帮助清理这些物体?
采纳答案by jalf
The problem with this is that you're really creating an implicit coupling between the object and the World class.
这样做的问题是您实际上是在对象和 World 类之间创建隐式耦合。
If I try to call Update() outside the World class, what happens? I might end up with the object being deleted, and I don't know why. It seems the responsibilities are badly mixed up. This is going to cause problems the moment you use the Fire class in a new situation you hadn't thought of when you wrote this code. What happens if the object should be deleted from more than one place? Perhaps it should be removed both from the world, the current map, and the player's inventory? Your Update function will remove it from the world, and then delete the object, and the next time the map or the inventory tries to access the object, Bad Things Happen.
如果我尝试在 World 类之外调用 Update(),会发生什么?我可能最终会删除对象,但我不知道为什么。责任似乎错综复杂。当您在编写此代码时没有想到的新情况下使用 Fire 类时,这将导致问题。如果应该从多个地方删除对象,会发生什么情况?也许它应该从世界、当前地图和玩家的库存中移除?您的 Update 函数会将其从世界中移除,然后删除该对象,下次地图或库存尝试访问该对象时,坏事就会发生。
In general, I'd say it is very unintuitive for an Update() function to delete the object it is updating. I'd also say it's unintuitive for an object to delete itself. The object should more likely have some kind of way to fire an event saying that it has finished burning, and anyone interested can now act on that. For example by removing it from the world. For deleting it, think in terms of ownership.
一般来说,我会说 Update() 函数删除它正在更新的对象是非常不直观的。我还想说一个对象删除自己是不直观的。对象应该更有可能有某种方式来触发一个事件,说它已经完成燃烧,任何感兴趣的人现在都可以对此采取行动。例如,将其从世界中移除。要删除它,请考虑所有权。
Who owns the object? The world? That means the world alone gets to decide when the object dies. That's fine as long as the world's reference to the object is going to outlast an other references to it. Do you think the object own itself? What does that even mean? The object should be deleted when the object no longer exists? Doesn't make sense.
谁拥有对象?世界?这意味着只有世界才能决定物体何时死亡。只要世界对对象的引用比对它的其他引用的持续时间更长,那就没问题了。你认为对象拥有自己吗?那有什么意思?当对象不再存在时,是否应该删除该对象?没有意义。
But if there is no clearly defined single owner, implement shared ownership, for example using a smart pointer implementing reference counting, such as boost::shared_ptr
但是如果没有明确定义的单一所有者,则实现共享所有权,例如使用实现引用计数的智能指针,例如 boost::shared_ptr
But having a member function on the object itself, which is hardcoded to remove the object from onespecific list, whether or not it exists there, and whether or not it also exists in any other list, and also delete the object itself regardless of which references to it exist, is a bad idea.
但是在对象本身上有一个成员函数,它被硬编码以从一个特定的列表中删除该对象,无论它是否存在,以及它是否也存在于任何其他列表中,并且也删除对象本身,无论哪个对它的引用存在,是一个坏主意。
回答by abelenky
You have made Update
a virtual function, suggesting that derived classes may override the implementation of Update
. This introduces two big risks.
你做Update
了一个虚函数,暗示派生类可能会覆盖Update
. 这带来了两大风险。
1.) An overridden implementation may remember to do a World.Remove
, but may forget the delete this
. The memory is leaked.
1.) 一个被覆盖的实现可能会记得做 a World.Remove
,但可能会忘记delete this
. 内存泄露了。
2.) The overridden implementation calls the base-class Update
, which does a delete this
, but then proceeds with more work, but with an invalid this-pointer.
2.) 重写的实现调用 base-class Update
,它执行 a delete this
,但随后继续进行更多工作,但使用无效的 this 指针。
Consider this example:
考虑这个例子:
class WizardsFire: public Fire
{
public:
virtual void Update()
{
Fire::Update(); // May delete the this-object!
RegenerateMana(this.ManaCost); // this is now invaild! GPF!
}
}
回答by Rob deFriesse
The delete will fail and may crash the program if the object was notallocated and constructed with new. This construct will prevent you from instantiating the class on the stack or statically. In your case, you appear to be using some kind of allocation scheme. If you stick to that, this might be safe. I certainly wouldn't do it, though.
如果对象未分配并使用 new 构造,则删除将失败并可能使程序崩溃。此构造将阻止您在堆栈上或静态地实例化类。在您的情况下,您似乎正在使用某种分配方案。如果你坚持这一点,这可能是安全的。不过,我当然不会这样做。
回答by 1800 INFORMATION
There is nothing really wrong with objects deleting themselves in C++, most implementations of reference counting will use something similar. However, it is looked on as a slightly esoteric technique, and you will need to keep a really close eye on the ownership issues.
在 C++ 中删除自己的对象并没有什么错,大多数引用计数的实现都会使用类似的东西。然而,它被视为一种稍微深奥的技术,您需要密切关注所有权问题。
回答by Johannes Schaub - litb
It certainly works for objects created by new
and when the caller of Update
is properly informed about that behavior. But i would avoid it. In your case, the ownership clearly is at the World, so i would make the world delete it. The object does not create itself, i think it should also not delete itself. Can be very surprising if you call a function "Update" on your object, but then suddenly that object is not existing anymore without World doing anything out of itself (apart from Removing it out of its list - but in another frame! The code calling Update on the object will not notice that).
它当然适用于由new
的调用者创建的对象以及当 的调用者Update
正确了解该行为时。但我会避免它。在你的情况下,所有权显然在世界,所以我会让世界删除它。该对象不会创建自己,我认为它也不应该删除自己。如果您在对象上调用函数“更新”可能会非常令人惊讶,但是突然间该对象不再存在,而 World 不自行执行任何操作(除了将其从列表中删除 - 但在另一个框架中!代码调用对象上的更新不会注意到)。
Some ideas on this
关于这个的一些想法
- Add a list of object references to World. Each object in that list is pending for removal. This is a common technique, and is used in wxWidgets for toplevel windows that were closed, but may still receive messages. In idle time, when all messages are processed, the pending list is processed, and objects are deleted. I believe Qt follows a similar technique.
- Tell the world that the object wants to be deleted. The world will be properly informed and will take care of any stuff that needs to be done. Something like a
deleteObject(Object&)
maybe. - Add a
shouldBeDeleted
function to each object which returns true if the object wishes to be deleted by its owner.
- 将对象引用列表添加到 World。该列表中的每个对象都待删除。这是一种常见的技术,在 wxWidgets 中用于已关闭但仍可能接收消息的顶级窗口。在空闲时间,当处理完所有消息时,处理挂起列表,并删除对象。我相信 Qt 遵循类似的技术。
- 告诉全世界该对象要被删除。世界将得到适当的通知,并将处理任何需要做的事情。像一个
deleteObject(Object&)
可能的东西。 shouldBeDeleted
向每个对象添加一个函数,如果该对象希望被其所有者删除,则该函数返回 true。
I would prefer option 3. The world would call Update. And after that, it looks whether the object should be deleted, and can do so - or if it wishes, it remembers that fact by adding that object to a pending-removal list manually.
我更喜欢选项 3。世界会调用更新。之后,它会查看对象是否应该被删除,并且可以这样做——或者如果它愿意,它会通过手动将该对象添加到待删除列表来记住这一事实。
It's a pain in the ass when you can't be sure when and when not to be able to access functions and data of the object. For example, in wxWidgets, there is a wxThread class which can operate in two modes. One of these modes (called "detachable") is that if its main function returns (and the thread resources should be released), it deletes itself (to release the memory occupied by the wxThread object) instead of waiting for the owner of the thread object to call a wait or join function. However, this causes severe headache. You can never call any functions on it because it could have been terminated at any circumstances, and you can not have it created not with new. Quite some people told me they very much dislike that behavior of it.
当您不能确定何时以及何时不能访问对象的函数和数据时,这是一件很痛苦的事情。例如,在 wxWidgets 中,有一个 wxThread 类,它可以在两种模式下运行。其中一种模式(称为“可分离”)是如果它的主函数返回(并且线程资源应该被释放),它会删除自己(释放wxThread对象占用的内存)而不是等待线程的所有者对象调用等待或加入函数。然而,这会导致严重的头痛。你永远不能在它上面调用任何函数,因为它可能在任何情况下被终止,而且你不能不使用 new 创建它。相当多的人告诉我,他们非常不喜欢它的这种行为。
The self deletion of reference counted object is smelling, imho. Let's compare:
引用计数对象的自我删除是有味道的,恕我直言。我们来比较一下:
// bitmap owns the data. Bitmap keeps a pointer to BitmapData, which
// is shared by multiple Bitmap instances.
class Bitmap {
~Bitmap() {
if(bmp->dec_refcount() == 0) {
// count drops to zero => remove
// ref-counted object.
delete bmp;
}
}
BitmapData *bmp;
};
class BitmapData {
int dec_refcount();
int inc_refcount();
};
Compare that with self-deleting refcounted objects:
将其与自删除 refcounted 对象进行比较:
class Bitmap {
~Bitmap() {
bmp->dec_refcount();
}
BitmapData *bmp;
};
class BitmapData {
int dec_refcount() {
int newCount = --count;
if(newCount == 0) {
delete this;
}
return newCount;
}
int inc_refcount();
};
I think the first is so much nicer, and i believe well designed reference counted objects do notdo "delete this", because it increases coupling: The class using the reference counted data has to know and remember about that the data deletes itself as a side-effect of decrementing the reference-count. Note how "bmp" becomes possibly a dangling pointer in ~Bitmap's destructor. Arguably, not doing that "delete this" is much nicer here.
我认为第一个更好,我相信精心设计的引用计数对象不会“删除它”,因为它增加了耦合:使用引用计数数据的类必须知道并记住数据将自身删除为减少引用计数的副作用。请注意“bmp”如何可能成为 ~Bitmap 的析构函数中的悬空指针。可以说,在这里不做“删除这个”要好得多。
Answer to a similar question "What is the use of delete this"
回答类似问题“删除这个有什么用”
回答by hacken
I prefer a stricter ownership model. The world should own the objects in it and be responsible for cleaning them up. Either have world remove do that or have update (or another function) return a value that indicates that an object should be deleted.
我更喜欢更严格的所有权模式。世界应该拥有其中的对象并负责清理它们。要么让 world remove 这样做,要么让 update(或其他函数)返回一个值,该值指示应该删除一个对象。
I am also guessing that in this type of pattern, you are going to want to reference count your objects to avoid ending up with dangling pointers.
我也猜测在这种类型的模式中,你会想要引用计数你的对象以避免以悬空指针结束。
回答by Adam Tegen
That's a fairly common reference counting implementation and I've used that successfully before.
这是一个相当常见的引用计数实现,我以前成功地使用过它。
However, I've also seen it crash. I wish I could remember the circumstances for the crash. @abelenkyis one place I have seen it crash.
但是,我也看到它崩溃了。我希望我能记住坠机的情况。@abelenky是我见过它崩溃的地方。
It might have been where you further subclass Fire
, but fail to create a virtual destructor (see below). When you don't have a virtual destructor, the Update()
function will call ~Fire()
instead of the appropriate destructor ~Flame()
.
它可能是您进一步子类化的地方Fire
,但未能创建虚拟析构函数(见下文)。当您没有虚拟析构函数时,该Update()
函数将调用~Fire()
而不是适当的析构函数~Flame()
。
class Fire : Object
{
public:
virtual void Update()
{
if(age > burnTime)
{
world.Remove(this);
delete this; //Make sure this is a virtual destructor.
}
}
};
class Flame: public Fire
{
private:
int somevariable;
};
回答by JohnMcG
Others have mentioned problems with "delete this." In short, since "World" manages the creation of Fire, it should also manage its deletion.
其他人提到了“删除这个”的问题。总之,既然“世界”管理着火的创建,它也应该管理它的删除。
One other problem is if the world ends with a fire still burning. If this is the only mechanism through which a fire can be destroyed, then you could end up with an orphan or garbage fire.
另一个问题是,如果世界以火仍在燃烧而结束。如果这是摧毁火灾的唯一机制,那么您最终可能会发生孤儿火灾或垃圾火灾。
For the semantics you want, I would have an "active" or "alive" flag in your "Fire" class (or Object if applicable). Then the world would on occasion check its inventory of objects, and get rid of ones that are no longer active.
对于您想要的语义,我会在您的“Fire”类(或 Object,如果适用)中有一个“active”或“alive”标志。然后,世界有时会检查其对象清单,并清除不再活动的对象。
--
——
One more note is that your code as written has Fire privately inheriting from Object, since that is the default, even though it is much less common than public inheiritance. You should probably make the kind of inheritance explicit, and I suspect you really want public inheritiance.
还有一点要注意的是,您编写的代码已经从 Object 私下继承了 Fire,因为这是默认设置,尽管它比公共继承要少得多。您可能应该明确继承的类型,我怀疑您真的想要公共继承。
回答by Ron Warholic
It's not generally a good idea to do a "delete this" unless necessary or used in a very straightforward way. In your case it looks like the World can just delete the object when it is removed, unless there are other dependencies we're not seeing (in which case your delete call will cause errors since they aren't notified). Keeping ownership outside your object allows your object to be better encapsulated and more stable: the object won't become invalid at some point simply because it chose to delete itself.
除非有必要或以非常直接的方式使用,否则执行“删除此”通常不是一个好主意。在您的情况下,看起来 World 可以在删除对象时删除该对象,除非有我们没有看到的其他依赖项(在这种情况下,您的删除调用将导致错误,因为它们没有得到通知)。将所有权保留在您的对象之外可以让您的对象得到更好的封装和更稳定:该对象不会因为它选择删除自身而在某些时候变得无效。
回答by Yes - that Jake.
I don't this there's anything inherently wrong with an object deleting itself, but another possible method would be to have the world be responsible for deleting objects as part of ::Remove (assuming all Removed objects were also deleted.)
我不认为对象删除本身有任何固有的错误,但另一种可能的方法是让世界负责删除对象作为 ::Remove 的一部分(假设所有 Removed 对象也被删除。)