所以,我有一个基类指针列表:
list<Base*> stuff;
然后,在某个时刻,其中一个对象将浏览所有其他对象。
Base * obj = ...; // A pointer from the 'stuff'-list.
for (list<Base*>::iterator it = stuff.begin(); it != stuff.end(); it++)
{
if (obj == *it)
continue;
// Problem scenario is here
obj->interact(it);
}
我想要实现的是,取决于派生类型obj
和*it
是什么,它们将彼此不同地互动,即DerivedA
如果它与{进行交互则会自我毁灭{1}},但仅当DerivedB
设置了属性DerivedB
时。如下所示:
bool c = true;
在编译时,它们都是struct Base
{
virtual void interact(Base * b); // is always called
};
struct DerivedA : public Base
{
virtual void interact(Base * b){} // is never called
virtual void interact(DerivedB * b) // is never called
{
if (b->c)
delete this;
}
};
struct DerivedB : public Base
{
bool c = false;
virtual void interact(Base * b){} // is never called
virtual void interact(DerivedA * a) // is never called
{
c = true;
}
};
// and many many more Derived classes with many many more specific behaviors.
- 指针,并且无法相互调用并期望该类型神奇地出现。如果这是单向关系,即我知道其中一种类型,我可以使用Visitor pattern。我相信我应该使用某种Mediator pattern,但无法弄清楚,因为调解员也会持有Base
指针,因此它不会产生任何影响。
我还没有得到如何继续......任何人的线索?
背景
我正在创建一个游戏,这个问题源于Base
类跟踪它的内容,即Room
当前在房间里的内容。
有时,物体移动(例如,玩家)。然后,房间将遍历即将被移动的地板砖(上面的环)上的所有物体,并将检查物体是否将彼此相互作用。
例如,如果它是GameObject
Troll
会想要伤害它。或者他只想伤害来自任何其他“团队”的任何Player
(Character
和Troll
都来自Player
)(可以从函数Character
,所有getAlignment()
都实现了。
答案 0 :(得分:4)
如果可以,请抓住“更有效的C ++”的副本,并查看第31项关于实现多个调度的内容,这基本上就是您在这里寻找的内容。迈耶斯讨论了解决问题的几种方法及其各种权衡。 (他甚至以游戏为例。)
然而,他给出的最佳建议可能是尝试重新设计代码以避免需要此功能。在文本中,还探讨了非成员函数方法,其中还有一个额外的好处,即消除了描述交互的每个函数应该属于哪个对象的问题。
答案 1 :(得分:2)
我认为您的建议(使用Base::interact
)功能几乎已完成。似乎唯一缺少的部分就是:
在Base
中,您需要为子类型提供所有interact
重载。考虑Base
结构的此扩展名:
struct DerivedA;
struct DerivedB;
struct Base
{
virtual void interact(Base * b); // *<->Base interaction
virtual void interact(DerivedA * da); // *<->DerivedA interaction
virtual void interact(DerivedB * db); // *<->DerivedB interaction
};
在C ++中实现双重调度是一件痛苦的事情:如果你添加一个新的子类型,你必须触及层次结构的基础。
答案 2 :(得分:0)
首先:为什么使用struct
代替class
?
第二:如果您使用class
代替struct
,您可以(必须)执行以下操作:
class Base
{
virtual void interact(Base * b); // see the VIRTUAL word (explained down)
};
class DerivedA : public Base
{
virtual void interact(DerivedB * b)
{
if (b->c)
delete this;
}
};
class DerivedB : public Base
{
bool c = false;
virtual void interact(DerivedA * a)
{
c = true;
}
};
使用virtual
关键字是你需要的(我猜)。如果你将方法定义为virtual
,你会告诉“嘿!这可能已被覆盖了某个地方”所以..当你编码时:
DerivedA* a = new DerivedA();
DerivedB* b = new DerivedB();
a.interact(b); // here instead of calling Base::interact(Base*) call the override method in DerivedA class (because is virtual)
编辑:
忘掉答案..(没看到virtual
)的评论
编辑2:
请参阅catwalk和Frerich Raabe个答案。
答案 3 :(得分:0)
您的interact()
函数没有相同的签名:在派生类中,它们也应该是
virtual void interact(Base * b);
virtual
当然是可选的,但为了清楚起见,我会把它放在那里。
要了解DerivedA :: interact()是否应该对其参数执行某些操作,您可以在基类中实现其他虚函数:
virtual canDoX(Base * b);
virtual canDoY(Base * b);
然后在派生的实现中,它看起来像这样:
// DerivedA
void interact(Base * b)
{
if (b->canDoX() && b->c)
delete this;
}
// DerivedB
void interact(Base * b)
{
if(b->canDoY())
c = true;
}
<强>更新强>
既然你喜欢Frerich Raabe的答案,让我解释为什么我认为我的方法更好一点
根据他的建议,必须为基础中的每个派生类创建一个interact()
方法,并为所有其他可以与某个类交互的派生类。
使用我的解决方案,必须为某些属性添加方法,这些方法也可以组合使用。
如果您有Troll,它会在true
方法中返回canBeKilled()
。一个苹果canBeEaten()和一个美味的野生动物canBeKilled()
然后canBeEaten()
如果可以组合属性,则必须添加更少的功能。
此外:如果巨魔喝了一些长生不老药使其在一段时间内无法使用,它会返回canBeKilled() == false
就是这样。您不必在每个其他交互类中检查isInvulnerable()
标志。
答案 4 :(得分:0)
您需要在层次结构之上实现彼此交互的所有可能的类型组合作为虚函数。 以下是测试所有可能的交互的示例:
#include<iostream>
void say(const char *s){std::cout<<s<<std::endl;}
struct DerivedA;
struct DerivedB;
struct Base{
virtual void interact(Base *b) = 0;
virtual void interactA(DerivedA *b) = 0;
virtual void interactB(DerivedB *b) = 0;
};
struct DerivedA : public Base
{
virtual void interact(Base *b){
b->interactA( this );
}
virtual void interactA(DerivedA *b){
say("A:A");
}
virtual void interactB(DerivedB *b){
say("A:B");
}
};
struct DerivedB:public Base{
virtual void interact(Base *b){
b->interactB( this );
}
virtual void interactA(DerivedA *b){
say("B:A");
}
virtual void interactB(DerivedB *b){
say("B:B");
}
};
void interact(Base *b1,Base *b2){
b1->interact( b2 );
}
main(){
Base *a = new DerivedA;
Base *b = new DerivedB();
interact(a,b);
interact(b,a);
interact(a,a);
interact(b,b);
}
答案 5 :(得分:-2)
我认为您的问题已经过充分讨论了 Scott Meyer的Effective C ++如下所示:
规则:不要尝试使用基类指针访问派生类对象的数组 - &gt;结果将是未定义的。
我会举一个例子:
struct Base {
virtual void print(){// in base}
virtual~Base(){} //
}
struct Derived:public Base {
virtual void print(){// in derived}
}
void foo(Base * pBase,int N) {
for(int i=0; i<N ; i++)
pBase[i].print(); // result will be undefined......
}
int main() {
Derived d[5];
foo(&d,5);
}
这种行为的原因是编译器在sizeof(Base)字节跳转时找到下一个元素....
我认为你明白我的观点......