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++?
我的结论是:
引用不能重新绑定,必须在初始化时绑定,然后行:
rBase = rDeriv;
仅触发 stBase 复制运算符,不会重新绑定 rBase。
- 引用可以看作类型化变量的别名 => 不能通过引用实现多态性。
考虑以下代码:
#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++?
我的结论是:
引用不能重新绑定,必须在初始化时绑定,然后行:
rBase = rDeriv;
仅触发 stBase 复制运算符,不会重新绑定 rBase。
- 引用可以看作类型化变量的别名 => 不能通过引用实现多态性。