如果派生类作为基类返回,如何访问派生类的方法?

Posted

技术标签:

【中文标题】如果派生类作为基类返回,如何访问派生类的方法?【英文标题】:How can I access a derived class' methods if it is returned as the base class? 【发布时间】:2019-02-07 02:46:14 【问题描述】:

我正在创建一个允许您将状态添加到堆栈中的状态机。以下是它包含的几个方法:

void pushState(State* state);

State* getCurrentState();

State 只是一个用于创建派生类的基类。所以我创建了一个名为GameState 的派生类,它有一个名为foo 的独特方法。如果我执行以下操作,它不会让我访问 foo,因为“类 State 不存在名为 'foo' 的方法。

State* currentState = myStateMachine.getCurrentState();

我也尝试将State* 更改为GameState*,但它不起作用,因为getCurrentState() 返回类型State。当我实际上不知道当前的派生状态是什么时,如何获取堆栈上的当前状态?

我也不知道为什么它允许我将GameState 推送到期待State 的堆栈上,但不允许我将它作为GameState 检索。相反,它让我在State 检索它。

【问题讨论】:

你如何从State 导出GameState?您是否使用了virtual 关键字? @thb 我刚刚做了class GameState : public State 您是否在 C++ 书籍中阅读过如何创建虚函数? 创建一个 MCVE (***.com/help/mcve) 以便我们可以真正看到您编写的代码,而不是从您的描述中猜测。 【参考方案1】:

试试这个:

#include <iostream>

namespace 
    class State 
    public:
        virtual int foo() const = 0;
    ;
    class GameState : public State 
    public:
        int foo() const  return 5; 
    ;


int main() 
    const GameState s;
    std::cout << s.foo() << "\n";
    const State *const p = &s;
    std::cout << p->foo() << "\n";
    return 0;

对于GameState要替换C++中State的方法foo(),该方法必须是虚的。原因是,默认情况下,为了提高效率,C++ 编译器会在编译时计算foo() 的地址。 virtual 关键字通过指向列出GameState 类型的虚方法的结构的指针来间接寻址。

您的问题是,除非foo() 是虚拟的,否则您的计算机无法在运行时判断State * 对象是指向State 对象还是GameState 对象。

但是,如果foo() 是虚拟的,那么包括GameState 对象在内的每个State 对象都包含一个指向隐藏vtable 的指针。State 类型 em> 有一个 vtable。 GameState 类型有另一个 vtable。 每个 vtable 引用正确的函数 foo()

[示例中的= 0 顺便通知编译器不会为State 对象提供任何实际函数foo()。您可以在代码中省略= 0,如果愿意,可以提供State::foo()。]

【讨论】:

【参考方案2】:

您应该将类​​型 State* 转换为 GameState*

我的意思是:

State* currentState = myStateMachine.getCurrentState();

应该是

// if State class don't have any virtual method, you should use static_cast<>
GameState* currentState = static_cast<GameState*>(myStateMachine.getCurrentState());

// if State class has at least virtual method, you should use dynamic_cast<>
GameState* currentState = dynamic_cast<GameState*>(myStateMachine.getCurrentState());

会有用的

【讨论】:

Ofc 你应该不喜欢 c 风格的演员表,而是写正确的演员表,如果getCurrentState 改变它的回报或GameState 停止继承State 将节省麻烦 使用dynamic_cast并在使用前检查结果是否为空

以上是关于如果派生类作为基类返回,如何访问派生类的方法?的主要内容,如果未能解决你的问题,请参考以下文章

给定一个基类作为参数,如果传递了派生类,我如何使用 op<< 重载来打印派生类的特征?

5继承与派生2-访问控制

继承和派生

使用基类对象访问仅派生类的方法

C++ 派生模板类:访问实例的受保护成员

实现基类方法以访问派生类的属性