C++,使用基类指针存储子类并使用派生类方法的策略

标签 c++ inheritance

基类是程序中各个类的基础,它提供了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 添加新函数不太容易受到脆弱基类综合症的影响)。好处是,您可以大幅更改 PersonAnimal 的界面,而不必更新 Base。根据新的需求进行扩展相当容易,并且避免了向下转换(这可能更容易被误用)。这使得设计比类型之间的转换更加明确什么是合法行为,并避免了潜在的 RTTI 要求(如果出于某种原因需要考虑)。

关于C++,使用基类指针存储子类并使用派生类方法的策略,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33897688/

相关文章:

c++ - std::map - 如何更改键排序?

javascript - 访问派生类中使用 Wea​​kMap 定义的私有(private)变量

c++ - 单例的每个核心设计的 IO 服务

c++ - 为什么我的 LP 只能得到整数解?

c++ - 在可变上下文中使用抽象类时如何实现抽象方法

C++ 跨平台超时读取

java - 发现它指向什么继承类

c# - 无法实现接口(interface)成员,因为它没有 List<IInterface> 的匹配返回类型

Ruby:创建不可继承的类方法

ruby - 具有类继承的错误未初始化常量