Gen*_*ror 24 c++ oop memory-management self-destruction resource-cleanup
我在C#度过了最近4年,所以我对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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
在这里,我们有一个负责管理一组对象并定期更新它们的世界.火是一种可能在许多不同情况下被添加到世界中的物体,但通常是世界上已有的另一物体.Fire是唯一知道什么时候它已经烧毁的对象,所以目前我已将它自行删除.造成火灾的对象可能不再存在或相关.
这是一件明智的事情还是有更好的设计来帮助清理这些物体?
abe*_*nky 30
您已经创建Update了一个虚函数,建议派生类可以覆盖实现Update.这带来了两大风险.
1.)被覆盖的实现可能记得做一个World.Remove,但可能会忘记delete this.记忆泄露了.
2.)重写的实现调用基类Update,它执行a delete this,但随后继续执行更多工作,但使用无效的this-pointer.
考虑这个例子:
class WizardsFire: public Fire
{
public:
virtual void Update()
{
Fire::Update(); // May delete the this-object!
RegenerateMana(this.ManaCost); // this is now invaild! GPF!
}
}
Run Code Online (Sandbox Code Playgroud)
jal*_*alf 20
这个问题是你真的在对象和World类之间创建了一个隐式耦合.
如果我尝试在World类之外调用Update(),会发生什么?我可能最终将对象删除,我不知道为什么.似乎责任严重混乱.当您在编写此代码时没有想到的新情况下使用Fire类时,这将导致问题.如果要从多个地方删除对象会怎样?也许它应该从世界,当前地图和玩家的库存中删除?您的更新功能将从世界中删除它,然后删除该对象,并在下次地图或库存尝试访问该对象时,Bad Things Happen.
一般来说,我会说Update()函数删除它正在更新的对象是非常不直观的.我还说一个对象删除自己是不直观的.该对象应该更有可能采取某种方式来触发事件,说它已经完成了刻录,任何感兴趣的人现在都可以采取行动.例如,将其从世界中删除.要删除它,请考虑所有权.
谁拥有这个对象?世界?这意味着只有世界才能决定对象何时死亡.只要世界对该对象的引用比其他对它的引用更持久,那就没问题.你觉得这个物体属于自己吗?那有什么意思?当对象不再存在时,应删除该对象?没有意义.
但是,如果没有明确定义的单个所有者,则实现共享所有权,例如使用实现引用计数的智能指针,例如 boost::shared_ptr
但是在对象本身上有一个成员函数,它被硬编码以从一个特定列表中删除对象,它是否存在于那里,以及它是否也存在于任何其他列表中,并且还删除了对象本身而不管哪个对它的引用存在,是个坏主意.
小智 6
如果没有使用new分配和构造对象,删除将失败并可能导致程序崩溃.此构造将阻止您在堆栈上或静态地实例化类.在您的情况下,您似乎正在使用某种分配方案.如果坚持这一点,这可能是安全的.不过,我当然不会这样做.
我更喜欢更严格的所有权模式.世界应该拥有其中的物品并负责清理它们.要么具有世界删除功能,要么具有更新(或其他功能)返回指示应删除对象的值.
我也猜测在这种类型的模式中,你将要引用计数你的对象以避免以悬空指针结束.
它肯定适用于new在调用者Update被正确通知该行为时创建的对象.但我会避免它.在你的情况下,所有权显然是在世界,所以我会让世界删除它.该对象不会创建自己,我认为它也不应该删除自己.如果你在你的对象上调用一个函数"Update"可能会非常令人惊讶,但是如果没有World自己做任何事情(除了将它从列表中删除 - 但在另一个框架中,那么突然该对象不再存在!代码调用对象的更新将不会注意到).
关于这个的一些想法
deleteObject(Object&)可能的东西.shouldBeDeleted向每个对象添加一个函数,如果该对象希望被其所有者删除,则返回true.我更喜欢选项3.世界将会更新.之后,它会查看是否应删除该对象,并且可以这样做 - 或者如果愿意,它会通过手动将该对象添加到挂起删除列表来记住该事实.
当您无法确定何时何时无法访问对象的功能和数据时,这是一个痛苦的屁股.例如,在wxWidgets中,有一个wxThread类,它可以在两种模式下运行.其中一种模式(称为"可分离")是,如果其主函数返回(并且应该释放线程资源),它会删除自身(释放由wxThread对象占用的内存),而不是等待线程的所有者用于调用wait或join函数的对象.然而,这会导致严重的头痛.你永远不能在它上面调用任何函数,因为它可能在任何情况下都被终止了,你不能用new创建它.有些人告诉我他们非常不喜欢它的行为.
引用计数对象的自删除有气味,imho.让我们比较一下:
// 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();
};
Run Code Online (Sandbox Code Playgroud)
将其与自删除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();
};
Run Code Online (Sandbox Code Playgroud)
我认为第一个更好,并且我相信精心设计的引用计数对象不会 "删除此",因为它增加了耦合:使用引用计数数据的类必须知道并记住数据将自身删除为递减引用计数的副作用.注意"bmp"如何成为~Ditmap的析构函数中的悬空指针.可以说,不这样做"删除这个"在这里要好得多.
回答类似的问题"删除这个有什么用"
| 归档时间: |
|
| 查看次数: |
20653 次 |
| 最近记录: |