所以我目前正在研究我的OOP / C ++技能,偶然发现了以下情况:
我正在制作一个由实体组成的游戏,必须吃才能生存。他们可以吃其他实体或食物。为了实现这种行为,我创建了一个edible
接口,它强制每个实现类创建一个getNutritionalInformation()
方法来计算一个单位在宴会后的饱和度。
所以整个事情应该像代码一样工作:
std::unique_ptr<Entity> e1(new Entity);
std::unique_ptr<Entity> e2(new Entity);
std::cout << "Pre feasting energy: " << e1->getNutritionalInformation() << std::endl;
e1->eat(*e2);
std::cout << "After feasting energy: " << e1->getNutritionalInformation() << std::endl;
在此操作之后c1
的能量应该高于之前,此值在创建实体期间当前是随机分配的。但是为了模拟被吃实体的 death ,我想在它被吃掉时手动杀死它。我通过以下方式实现了这个目标:
void Entity::eat(Edible& subject) {
this->energy += subject.getNutritionalInformation();
delete &subject;
}
但最后这对我来说似乎有点脏,我真的想让智能指针以某种方式知道,它当前持有/指向的对象不再有效。有没有更清洁的方法来实现这一目标?我很确定我尝试这样做的方式非常hacky并且不被认为是正确的OOP编程。
提前感谢您的帮助。
答案 0 :(得分:12)
好吧,e2
“吃”e1
它实际上取得了它的所有权。具体而言,它必须拥有它,以便销毁它。所以这样做的方法实际上是这样的:
void Entity::eat(std::unique_ptr<Edible> subject) {
this->energy += subject->getNutritionalInformation();
}
而且......就是这样。主题将在范围结束时自动销毁。要使用此功能,您需要明确调用std::move
;这表示您将所有权从调用范围转移到e2。
std::unique_ptr<Entity> e1(new Entity);
std::unique_ptr<Entity> e2(new Entity);
std::cout << "Pre feasting energy: " << e1->getNutritionalInformation() << std::endl;
e1->eat(std::move(e2));
std::cout << "After feasting energy: " << e1->getNutritionalInformation( << std::endl;
请注意,在std::move
上调用e2
后,e2
无法再被假定为实际指向实体(因为已转让所有权)。
答案 1 :(得分:1)
您希望e2
的所有者在其变量上调用delete
以防止出现问题。
您可以通过两种方式实现这一目标:
e1->eat(std::move(e2));
void Entity::eat(std::unique_ptr<Edible> subject) {
this->energy += subject->getNutritionalInformation();
}
或者:
e1->eat(*e2);
e2.reset(nullptr);
在任何一种情况下,你的调用者(你的第一个例子中的函数)都知道e2
被操作破坏了,在我给出的第一个例子中,因为你移出了它,在第二个例子中因为它手动调用reset
(它会破坏旧指针)。
答案 2 :(得分:1)
当std::unique_ptr
超出范围时,会自动调用对象的析构函数。你不必担心它。
手动销毁对象可能会导致问题。在这种情况下,当eat()
手动销毁subject
对象时,拥有该对象的std::unique_ptr
不知道该对象已被销毁,并且当它再次尝试销毁该对象时将崩溃第二次。
答案 3 :(得分:0)
是的,请不要手动执行。 这实际上你做的几件事 not 中的一件事必须要关心。你的程序为你做到了! 规则是:一旦变量离开范围,它就会被抛弃(desconstructor得到调用)。除了动态分配的内存,你需要释放(删除)变量/ s。
此外,如果您手动删除变量,程序将崩溃:
{
Type variable;
delete variable;
} //Scope end: deconstructor get's called. ERROR: already deleated, undefined behaviour, crash. everything destroyed.
答案 4 :(得分:0)
另一种方法是让一些更高级别的经理控制实体的生命周期,所以吃饭时的e2只会做:e1-&gt; kill(); E1-&GT; eatenEnergy(能量); (降低它的营养价值,如果可能部分吃掉实体)。
等到实体经理轮到他们,循环通过所有实体(或对报告的信号作出反应,比如来自e1-&gt; kill()实施中的死亡事件)并释放那些死亡并完全被吃掉/腐烂的人/ etc(杀死那些饥饿的人......)。
如果您希望保持简单的基于事件的世界(如您当前的示例),或者您想要一些&#34;生命周期&#34;发生在世界上,而[内存/实例]管理集中在单一类中。
我认为集中管理器会使事情变得复杂一些,但是随后可能会更容易实现一些可能性,比如更好地调试初始/结束状态,优先处理某些实体处理,重用实体内存(没有动态)分配)等。
在这两种情况下你都应该事先解决一些角落情况(主要是竞争条件)如何解决,例如:
因此,实体的状态应设计为在这种情况下有效,并以期望的结果结束。