【问题标题】:Polymorphic pointer多态指针
【发布时间】:2018-04-21 12:59:03
【问题描述】:

我正在与 C++ 中的多态性作斗争。我有 2 个课程,HeroMainPlayerHero 是基类,MainPlayer 派生自 Hero。在Hero 我有一些变量和方法,MainPlayer 有一些额外的变量和方法。我正在尝试创建多态指针,就像:

Hero *player = new MainPlayer(constructor);

然后使用播放器我只能访问来自Hero 类的方法,否则会出现编译错误。我想问是否可以 通过这个多态指针使用MainPlayer 方法和变量:player

我的代码:

class Hero
{
public:
    Hero();
    virtual void fun() = 0;
private:
    int a, b;
};

class MainPlayer:
public Hero
{   
public:
    MainPlayer();
    void fun();
    void fun2();
private:
    int c;
};

int main()
{
    Hero *player = new MainPlayer(constructor);
    player->fun(); // works
    player->fun2(); //doesn't work
    return 0;
}

【问题讨论】:

  • 代码在呈现时最好理解,而不是描述。
  • 是的,有可能
  • 关于术语的一点:指针就是指针;没有“多态指针”这样的东西。指针可以指向多态类型的对象,这就是您所看到的。

标签: c++ oop pointers polymorphism


【解决方案1】:

从指向基类Hero 的指针只能调用函数并访问Hero 类的成员。

这有一个重要的例外,即基类的虚函数在派生类中被覆盖(与派生类MainPlayer中描述的签名相同),它将调用派生类的实现(即使它不包含关键字override)。

最后一个从MainPlayer创建的指向Hero的指针,可以被转换回MainPlayer的指针,然后你就可以从MainPlayer调用函数和访问成员,如下例所示:

Hero * h2 = new MainPlayer();
MainPlayer * h3 = dynamic_cast<MainPlayer*>(h2); 

if (h3 != nullptr)
{
    h3->fun2();
}

delete h2;

最后别忘了删除你分配的指针


编辑:

override 说明符虽然不是必需的,但可以帮助您确保确实覆盖了基类中的函数。如果基类没有声明具有相同签名的虚函数,程序将无法编译。下面是这个概念的一个简单示例:

struct A
{
    virtual void foo();
    void bar();
};

struct B : A
{
    void foo() const override; // Error: B::foo does not override A::foo
                               // (signature mismatch)
    void foo() override; // OK: B::foo overrides A::foo
    void bar() override; // Error: A::bar is not virtual
};

【讨论】:

  • 值得编辑以指出 override 关键字的真正作用以及您应该使用它的原因。
  • 我已经编辑了答案以澄清解释“重要异常”的虚函数的覆盖,而不仅仅是签名(即,如果基类函数不是虚拟的,它会尽管签名相同,但调用基类版本而不是派生版本)。我同意@user4581301 :很高兴解释使用override 关键字的好处。
【解决方案2】:

这是可能的。这是一个例子。 h2 指向 Hero 但它使用的是 MainPlayer 中的方法。

#include <iostream>
#include <string>

class Hero
{
public:
    virtual std::string whoAmI() const { return "The Hero"; }
};

class MainPlayer : public Hero
{
public:
    std::string whoAmI() const override { return "The Main Player"; }
};

int main()
{

    Hero h1;
    std::cout << h1.whoAmI() << std::endl;
    Hero *h2 = new MainPlayer;
    std::cout << h2->whoAmI() << std::endl;

    return 0;
}

打印:

The Hero
The Main Player

【讨论】:

  • 是的,但我想使用派生类的方法,其名称尚未在基类中声明。
  • @Hunteerq 无法完成。 Hero 只知道 Hero 的东西。它不知道未来的扩展。
【解决方案3】:

当通过指向Hero 的指针访问MainPlayer 时,唯一暴露的接口是Hero 的接口,编译器只能利用Hero 已知的功能。

如何解决这个问题(按我个人喜好排序):

在需要MainPlayer 的附加功能的情况下,避免将MainPlayer 作为Hero 进行交互。 MainPlayer 像其他人一样攻击并受到伤害(或多或少归功于覆盖的可能性),但如果只有 MainPlayer 可以 SwingFromChandelier,那么只有 MainPlayer 应该有机会这样做。在这种情况下,大部分时间MainPlayer 已经可以作为MainPlayer 使用。

抽象出公开额外功能的需要。例如,也许MainPlayer::fun 可以调用MainPlayer::fun2 来做额外的MainPlayer 事情。 MainPlayerHero::Attack 的覆盖可以调用BerserkRampage 特殊攻击函数,无需将BerserkRampage 暴露给任何人。

Heros 接口可能未指定。考虑用额外的虚函数扩展Hero

dynamic_castHero *MainPlayer *,验证结果不是nullptr,并在新生成的MainPlayer *上调用MainPlayer功能

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-28
    • 2014-06-11
    相关资源
    最近更新 更多