C++ 多态性没有按预期工作

C++ Polymorphism does not work as expected

考虑以下代码:

#include <iostream>

class Base{
public:
    int iB;
    Base() :iB(0){};
    virtual ~Base(){}
    virtual void foo(Base&, const int&, const int&) const = 0;
};


class Derived : public Base{
public:
    int iD;
    Derived() :iD(1){};
    void foo(Derived&, const int&, const int&) const;
};
void Derived::foo(Derived& d, const int& i1, const int& i2) const{ 
    std::cout << d.iD; 
}


class BaseWrap{
public:
    BaseWrap(){};
    virtual ~BaseWrap(){};
    virtual Base* bar() const = 0;
protected:
    Base* b;
};


class DerivedWrap : public BaseWrap{
public:
    DerivedWrap(){};
    Derived* bar() const;
private:
    Derived* d;
};
Derived* DerivedWrap::bar() const { 
    return new Derived; 
}

int main(){

    return 0;
}

这会导致编译器错误 "Error: object of abstract class type "不允许派生“纯虚函数 Base::foo”没有覆盖程序。 我假设由于多态性,我总是可以将指针指向派生的 class ,其中需要指向 Base class 的指针。无论如何,我尝试将 Derived class 更改为以下内容:

class Derived : public Base{
public:
    int iD;
    Derived() :iD(1){};
    void foo(Base&, const int&, const int&) const;
};
void Derived::foo(Base& d, const int& i1, const int& i2) const{
    std::cout << d.iD; 
}

但是,现在我收到错误 "Error: class "Base" has no member "iD".

编辑:

foo 对 derived 进行引用,因为在我的实际实现中,即我希望能够这样做:

Derived d1;
Derived d2;
d1.foo(d2, 0, 1);

此外,我可能应该更清楚我实际要问的是什么。我意识到删除纯虚函数声明

virtual void foo(Base&, const int&, const int&) const = 0;

修复了问题。但是,在所有派生的 class 实现中,代码完全相同,只是第一个参数的类型有所不同(派生 classes 来自 Base)。所以感觉应该有一个纯虚函数来强制 foo 的存在。

问题

在你的基础class中你定义了纯虚成员函数:

virtual void foo(Base&, const int&, const int&) const = 0;

但您在派生函数中提供了另一个签名:

void foo(Derived&, const int&, const int&) const;

所以你在派生的class中多了一个成员函数(重载:同名但参数类型不同),但仍然继承了纯虚函数。所以派生的 class 和基础的 class 一样抽象,你不能实例化它。

解决方案

在派生的 class 中更改签名,使其与纯虚基成员函数相匹配:

void foo(Base&, const int&, const int&) const;

顺便说一下,每当你使用虚函数时,使用 override 关键字在编译时发现这些细微的错误,即使是普通的虚函数也是如此:

void foo(Base&, const int&, const int&) const override;

更多信息

确实,一旦您使用 Base 参数定义了 foo(),就无法轻松使用 iD 成员。

第一个解决方案是使用 dynamic_cast 告诉您的代码该基实际上是一个派生对象。当然你必须检查这个假设是否正确:

void Derived::foo(Base& d, const int& i1, const int& i2) const{ 
    Derived *dp = dynamic_cast<Derived*>(&d);
    if (dp) {
        std::cout << dp->iD; 
    }
}

但是,我不清楚的是为什么您首先需要这个。为什么不去掉这个第一个依赖类型的参数,改用当前对象的成员变量:

void Derived::foo(const int& i1, const int& i2) const{ 
    std::cout << iD; 
}

最终,我遇到了这个:What are the differences between a pointer variable and a reference variable in C++?

我的结论是:

  1. 引用不能重新绑定,必须在初始化时绑定,然后行:

    rBase = rDeriv;

仅触发 stBase 复制运算符,不会重新绑定 rBase。

  1. 引用可以看作类型化变量的别名 => 不能通过引用实现多态性。