应该删除的对象自己在C++?
-
22-08-2019 - |
题
我已经花了过去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;
}
}
}
在这里,我们有一个世界负责管理一个设置的对象和定期更新他们.火是一个对象,可能会添加到全世界在许多不同的情况,但是通常由另一个目已经在世界上。火是唯一的对象知道,当它已经烧掉了所以目前我把它删除本身。的对象创造的火灾很可能不再存在或相关的。
这是一个明智的事情要做,或是否有更好的设计,这将有助于清除这些对象?
解决方案
这里的问题是,你实际上是在创建对象和世界级之间的隐式耦合。
如果我尝试调用Update()创建世界一流之外,会发生什么?我可能最终与该对象被删除了,我不知道为什么。看来责任是严重混淆。这将导致问题您使用的防火级别中你有没有想过,当你写了这个代码的新形势的时刻。会发生什么,如果对象应该从多个地方被删除?或许它应该被删除都来自世界各地,目前的地图,玩家的存货?您的更新功能将会从世界中删除,然后删除该对象,下一次在地图或库存尝试访问该对象,不好的事情发生。
在一般情况下,我会说这是用于更新()函数来删除它被更新对象非常直观。我想也说这是直观的对象删除自身。 该对象应该更可能有某种方式来触发一个事件说,它已经完成燃烧,并有兴趣现在就可以对采取行动。例如,通过从世界中删除。对于删除,认为在所有权方面。
谁拥有该对象?世界?这意味着仅在世界变得当对象死亡来决定。只要世界的参考对象将拖垮一个其他引用它那很好。 你认为自己的对象本身?那有什么意思?当对象已不存在的对象应该被删除?没有意义。
但是,如果没有明确定义的单所有者,实现共享所有权,例如利用智能指针实现引用计数,如boost::shared_ptr
但是具有对象本身,它是硬编码到从删除对象的成员函数的一个特定的列表,它是否存在那里,并且无论其是否还存在于任何其他列表,并且还删除对象本身无论哪个对它的引用存在的,是一个好主意。
其他提示
您已经取得Update
一个虚拟函数,这表明派生类可以覆盖Update
的实施。这引入了两个大的风险。
1)。一个重写实现可以记得做一个World.Remove
,但可能忘记delete this
。存储器泄漏。
2)被覆盖的实现调用基类Update
,其中做了delete this
,但然后用更多的工作继续进行,但有一个无效的这个指针。
考虑这个例子:
class WizardsFire: public Fire
{
public:
virtual void Update()
{
Fire::Update(); // May delete the this-object!
RegenerateMana(this.ManaCost); // this is now invaild! GPF!
}
}
有什么真的错对象删除自己在C ++中,引用计数的大部分实施将使用类似的东西。但是,看着一个稍微深奥的技术,您将需要保持一个非常密切关注的所有权问题。
在删除将失败,并且可能会崩溃程序,如果对象是不的分配,并用新的构建。这种结构可以防止你实例化堆栈或静态的类。在你的情况,你似乎是使用某种类型的分配方案。如果你坚持这一点,这可能是安全的。我肯定不会做,虽然。
我更喜欢更严格的所有权模型。世界应该持有它的对象,并负责清理起来。要么有世界删除这样做或有更新(或其它功能)返回一个值,该值指示对象应予以删除。
我也猜测,在这个模式的类型,你会想引用计数的对象,以避免与悬摆指针结束了。
它当然适用于创建的对象 new
及时来电的 Update
正确地了解这种行为。但是,我要避免它。在你的情况下,所有权显然是在世界各地,所以我将使世界上删除。的对象不创建自己,我认为它也不应该删除本身。可能是非常令人惊奇的如果你打电话的功能"更新的"对象上,但后来突然,对象是不现有的世界上再没有做出任何东西的本身(除了删除它从其名单,但在另一架!代码呼吁更新的对象,不会通知)。
一些想法在这
- 增加一个列表中的对象提到的世界。每个对象在该名单是待清除。这是一个共同的技术,是用于在个函数库和二进制文对于顶层的窗户被关闭,但仍可以接收信息。在空闲时间内,当所有信息进行处理,待名单进行处理,并对象是删除。我认为,脱下一个类似的技术。
- 告诉全世界,对象要被删除。世界将会适当告知与会照顾任何东西,需要做的事情。有点像
deleteObject(Object&)
也许。 - 增加一个
shouldBeDeleted
功能每个对象返回真的,如果对象的意愿被删除,通过它的主人。
我宁愿选项3。世界会呼吁更新。在这之后,它看起来对象是否应删除,并可以这样做,或者如果它愿意,它会记住这个事实由增加,对象为未决的除名单。
这是一个痛苦的屁股的时候你不能确定当时不可能访问的功能和数据的对象。例如,在个函数库和二进制文,有一个wxThread类可以在两种模式。这些模式之一(称为"拆卸")是,如果其主要功能将返回(以及在线资源应该被释放),它删除本身(以释放所占用的存储器wxThread对象)而不是等待主人的对象螺纹呼叫等待还是参加功能。然而,这导致严重头痛的问题。你可以永远不会打电话给任何功能,因为它可能已经终止在任何情况下,和你不能拥有它创造了新的。相当一些人告诉我他们非常不喜欢这种行为。
自删除的参考计对象是闻,恕我直言。让我们比较:
// 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();
};
相比之下,自删除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();
};
我认为第一个这么好得多,并且我认为,设计良好的基准计算的对象 不 做"删除",因为它增加了联:这类使用的参考数据已经知道并记住,数据删除自身作为一个副作用的递减的参数。注意如何"bmp"变得可能是一晃来晃去的指针在~位是析构函数。可以说,不这样做"删除这个"是非常好的在这里。
回答类似的问题 "有什么用删除这个"
这是一个相当普遍的引用计数实现,我已经使用了成功之前。
不过,我也看到了它崩溃。我希望我能记得的情况下崩溃。 @abelenky 是一个地方,我都看到了崩溃。
它可能已经在那里你进一步的子类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;
};
其他人已经提到有问题的“删除此”。总之,因为“世界”的消防管理的创作,也应该管理其删除。
另外一个问题是,如果世界上有一个火仍在燃烧结束。如果这是通过该火可能被破坏的唯一机制,则可以用一个孤儿或垃圾火灾结束。
有关你想要的语义,我会在你的“火”类“积极”或“活”标志(或如适用对象)。那么世界会偶尔检查它的对象的库存,并摆脱那些不再活跃的。
-
还有一个值得注意的是,作为编写代码有火从私有继承的对象,因为这是默认的,即使它是不是公共inheiritance常见的要少得多。你或许应该做出怎样继承明确的,我怀疑你真的希望公众inheritiance。
这不是一般的好主意,做一个“删除此”除非必要或以非常直接的方式使用。在你的情况下,它看起来像世界只需删除对象时,它被删除,除非有我们没有看到(在这种情况下,你的删除调用将导致错误,因为他们不通知)其他依赖。保持你的所有权对象之外允许你的对象是更好的封装和更稳定:对象将不会成为在某些时候无效,只是因为它选择了删除本身
我不知道这有什么内在的错误的对象删除本身,而是另一种可能的方法是拥有世界负责删除对象作为::删除部分(假设也会被删除,全部拆除的对象。)
删除这是非常危险的。没有什么“错误”的程度。这是合法的。但也有很多事情可以去错了,当你使用它,它应该谨慎使用。
考虑其中有人已经打开的指针或引用的对象的情况下,然后它进入并删除它本身。
在大多数情况下,我认为对象的生命周期应由创建它的同一个对象进行管理。它只是可以更容易地知道在哪里发生破坏。