基类是程序中各个类的基础,它提供了x_pos和y_pos。
Animal(为清楚起见已删除)和 Person 是 Base 的直接子类。
不同的人和动物可以被创建为动物和人的子类
在我的示例中,我创建了 Wang 类。
我有以下类(class)。
#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 指针来存储人或动物。(例如,地板有一个踩在其上的“Base”)
class Floor{
Base *bs;
public:
void haveFun(){
// bs->Wang::havefun();
// I want to achieve this goal
}
}
如何在不进行向下转换的情况下访问“人/动物”和“Wang”级别的方法
我可以在这里应用什么策略吗?
(我觉得访客模式可能适用于此,但我不知道如何应用)
最佳答案
这里使用双分派(dispatch)/访问者有点尴尬,因为为 Floor
提供的代码已经存储了 Base*
并且只有一个继承层次结构。在没有进行相当侵入性的设计更改的情况下,类型信息此时已经丢失了。
无论您使用什么,通常您都需要基于实际子类型的某种形式的分支。它可能是第二个虚拟调度,可能是查看类型信息的 if/else
语句,可能是尝试 dynamic_casts
并捕获 bad_cast
code> (用于引用)或根据 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;}
...
}
这将人/动物
耦合到Base
,但仅在符号级别。 Base
仍然可以通过其他方式(包括编译时 header 依赖项)与 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 要求(如果出于某种原因需要考虑)。
关于C++,使用基类指针存储子类并使用派生类方法的策略,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33897688/