基类是程序中每个类的基础,它提供了x_pos和y_pos。
Animal (为清晰而删除)和Person是Base的直接子类。
可以创建不同的人和动物作为动物和人的子类
在我的例子中,我创建了Wang类。
我有以下课程。
#include <iostream>
class Base{
protected:
int x_pos;
int y_pos;
public:
Base(int x,int y): x_pos(x), y_pos(y){};
virtual ~Base(){};
int getX(){return x_pos;};
int getY(){return y_pos;};
};
class Person : public Base{
protected:
char sex;
public:
Person(int x,int y,char sex):Base(x,y), sex(sex){};
virtual ~Person(){};
char getSex(){return sex;};
virtual void haveFun() = 0;
};
class Wang : public Person{
public:
Wang(int x,int y,char a): Person(x,y,a){};
~Wang(){};
void haveFun(){ std::cout << "Wang is coding" << std::endl;};
};
问题是,出于某种原因,我必须有一个Base指针来存储人或动物。(例如,一个地板有一个&#34; Base&#34;它会踩到它)>
class Floor{
Base *bs;
public:
void haveFun(){
// bs->Wang::havefun();
// I want to achieve this goal
}
}
如何在没有向下铸造的情况下访问Person / Animal和Wang级别的方法 我有什么策略可以在这里申请吗? (我觉得可以在这里应用vistor的模式,但我无法弄清楚如何)
答案 0 :(得分:1)
如果你想避免向下转换(这是一个合理的想法,虽然因为dynamic_cast&lt;&gt;将提供安全的向下转换,因此不是绝对必要的),你有两个选择:
1)声明您可能希望在基类中调用的所有方法,作为虚方法。定义那些不做任何事情或返回错误代码(或类似代码)的方法的基类实现,这样当你在不适当类型的对象上调用这些方法时,不会发生任何有害的事情。
或
2)在你的Floor类中保留多个指针;对于您可能想要保留的每种类型的对象都有一个。这样你就是这样的想要调用haveFun(),你知道你的Person对象可以通过(Person *)指针而不是(Animal *)指针访问。如果要强制执行每个楼层唯一一个对象约束,请务必在设置Person指针时清除Animal指针,反之亦然。
答案 1 :(得分:1)
在这里使用双重调度/访问者有点尴尬,因为为Floor
提供的代码已经存储Base*
,并且只有一个继承层次结构。在这一点上,类型信息已经丢失了,没有进行过多的侵入式设计更改。
无论您使用什么,通常您需要基于实际子类型的某种形式的分支。它可能是第二个虚拟分派,它可能是if/else
语句查看类型信息,它可能是dynamic_casts
的尝试,也可能是捕获bad_cast
(用于引用)或检查结果nullptr
指示。
在你的情况下,如果你想避免dynamic_cast
/ RTTI并且保持代码主要是原样,我实际上会建议这样的事情:
class Animal;
class Person;
class Base{
public:
...
virtual Person* person() {return nullptr;}
virtual Animal* animal() {return nullptr;}
...
}
class Person: public Base{
public:
...
Person* person() override {return this;}
...
}
class Animal: public Base{
public:
...
Animal* animal() override {return this;}
...
}
这会将Person/Animal
与Base
联系起来,但仅限于某种级别的符号。 Base
仍然可以以其他方式与Person/Animal
分离(包括编译时头依赖项)。
这确实意味着您必须为要分别处理的每个子类型添加代码Base
,但无论您使用什么(包括访问者),您通常都需要在某处执行此操作。
class Floor{
Base *bs;
public:
void haveFun(){
// If `bs` is a person, invoke `haveFun`.
Person* person = bs->person();
if (person)
person->haveFun();
}
}
这种方法的缺点是,您要通过指向Base*
的指针在子类型级别上添加的每个新子类型都需要向Base
添加新函数(尽管添加Base
的新功能不易受脆弱的基类综合症的影响。好处是,您可以相当大地更改Person
或Animal
的界面,而不必更新Base
。对新要求进行扩展是相当容易的,并且避免了下行(可能更容易被滥用)。这使得设计对于什么是合法行为比在类型之间进行更加明确,并且避免了潜在的RTTI要求(如果出于某种原因需要考虑)。