C++ C++;,使用基类指针存储子类并使用派生类的策略';方法
基类是程序中每个类的基,它提供x_pos和y_pos。C++ C++;,使用基类指针存储子类并使用派生类的策略';方法,c++,inheritance,C++,Inheritance,基类是程序中每个类的基,它提供x_pos和y_pos。 Animal(为清晰起见删除)和Person是基类的直接子类 不同的人和动物可以创建为动物和人的子类 在我的示例中,我创建了Wang类 我有以下课程 #include <iostream> class Base{ protected: int x_pos; int y_pos; public: Base(int x,int y): x_pos(x), y_pos(y){}; virtu
Animal(为清晰起见删除)和Person是基类的直接子类
不同的人和动物可以创建为动物和人的子类
在我的示例中,我创建了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;};
};
我如何才能在不向下投射的情况下访问人/动物和王级别的方法有什么策略可以在这里应用吗?
(我认为vistor的模式可能适用于这里,但我不知道如何使用)如果您想要避免向下投射(这是一个合理的选择,尽管这不是严格必要的,因为动态向下投射将提供安全的向下投射),您有两个选择: 1) 将基类中可能要调用的所有方法声明为虚拟方法。定义那些不执行任何操作或返回错误代码(或类似代码)的方法的基类实现,以便在对不适当类型的对象调用这些方法时,不会发生有害的情况 或
2) 在课堂上保持多个指针;对于您可能希望保存在其中的每种类型的对象,都有一个。这样,当您想调用haveFun()时,您就知道您的Person对象可以通过(Person*)指针而不是(Animal*)指针访问。如果您想强制每个楼层只有一个对象约束,请确保在设置人员指针时清除动物指针,反之亦然。在此处使用双重分派/访问者有点尴尬,因为为
楼层提供的代码已经存储了基本*
,并且只有一个继承层次结构。类型信息在这一点上已经有点丢失了,没有一个相当侵入性的设计更改
无论您使用什么,通常都需要基于实际子类型的某种形式的分支。它可能是第二个虚拟分派,可能是查看类型信息的if/else
语句,可能是尝试dynamic\u cast
,或者捕获bad\u cast
(用于引用),或者针对nullptr
检查指针的结果
在您的情况下,如果您希望避免dynamic\u 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*
的指针在子类型级别上处理添加的每个新子类型都需要向Base
添加新函数(尽管向Base
添加新函数并不容易受到脆弱基类综合症的影响)。这样做的好处是,您可以大大更改Person
或Animal
的界面,而不必更新Base
。它很容易根据新的需求进行扩展,并避免了向下转换(这可能更容易被误用)。这使得设计对于什么是合法行为比在类型之间转换更加明确,并且避免了潜在的RTTI要求(如果出于某种原因这是一个问题)。仍然存在编译错误。@PaulEvans更正。谢谢你不恰当地使用了继承。举例说明。。。。从什么时候起,“王”就变成了另一类人了?猫和动物之间的关系是显而易见的——猫是一种特殊类型的动物。然而,王只是一个特定个体的名字,而不是某个特定类型的人。@Peter我这样做是因为不同的人有不同的方式来享受乐趣()。在一些游戏中把王当作英雄。嗯,这是你的问题。英雄主义是一种情景属性(例如,面对火灾时的勇敢、因勇敢而获得奖励或特权等),而不是定义不同的人的子类。谢谢你的回答。如果我在Person和Wang之间再上一节课呢。例如,GoodPerson,它有一个字段(moneyDevate:int)和一个方法(getMoneyDevate():int)。我可以在基类中放一个虚拟的getMoneyVedacted():int吗?当然可以;可以让基类方法只返回0或其他值。当然,这样做的最终缺点是基类可能会开始变得非常混乱,有很多方法,但它会起作用。
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;}
...
}
class Floor{
Base *bs;
public:
void haveFun(){
// If `bs` is a person, invoke `haveFun`.
Person* person = bs->person();
if (person)
person->haveFun();
}
}