为什么抽象类不允许协方差?

时间:2021-07-19 02:27:45

标签: c++ interface abstract-class covariance

class GameObject {
public:
    virtual ~GameObject() {}
};

class Player: public GameObject {};

struct IGameController {
    virtual GameObject* GetPlayer() = 0;
};

class CameraOnlyController : public IGameController {
public:
    GameObject* GetPlayer() override { return nullptr; }
};

class PlayerController : public IGameController {
public:
    PlayerController() { player = new Player(); }
    ~PlayerController() { delete player; }
    Player* GetPlayer() override { return player; }
private:
    Player* player;
};

int main() {
    IGameController* playerController = new PlayerController();
    Player* player = playerController->GetPlayer();     // error: a value of type "GameObject *" cannot be used to initialize an entity of type "Player *"
    delete playerController;
}

如果我将控制器接口专门更改为 PlayerController,它确实可以编译

PlayerController* playerController = new PlayerController();

我知道 playerController 稍后可以像这样指向 CameraOnlyController

playerController = new CameraOnlyController();

但是既然在 Player* 播放器初始化时它没有,为什么会被阻止? 是编译器试图强制执行类型安全吗,我假设它当时知道 playerController 被分配给了 new PlayerController(),但是这样假设是错误的吗?

1 个答案:

答案 0 :(得分:2)

IGameController* playerController = new PlayerController();

playerController 的类型为 IGameController*

C++ 编译器类型检查记住有关 playerController 的任何其他内容。它忘记了它是从一个指向 PlayerController 的指针构造的事实。

Player* player = playerController->GetPlayer();

所以这里需要它允许知道的信息,即 playerController 是一个 IGameController*,并指出存在类型不匹配。

如果您希望编译器了解更多关于 playerController 的类型,您必须自己更改 playerControllertype。 C++ 编译器不会自动将 playerController 的类型扩展为它在确定代码行含义时所能知道的所有内容。

同时,C++编译器可以自由地遵循as-if规则,将playerController的类型去虚拟化。但是他们可能只会好像他们没有这样做(例如,使您的代码更快)。

存在允许对给定变量进行更广泛的类型推导的编程语言。 C++ 不是其中之一。

你可以这样做:

auto* playerController = new PlayerController();
auto* player = playerController->GetPlayer();
delete playerController;

在这种情况下,将使用各种变量的确切类型,或者

auto* playerController = new PlayerController();
Player* player = playerController->GetPlayer();
delete playerController;

验证 player 是您想要的类型。