C++ 为什么虚拟关键词是;持久的;在C++;?
假设我有一个函数print(),分为3个类a、B和C。 C继承自B继承自A。 关键字virtual仅用于A 为什么下面两个都在C中使用print() 这里的直觉是什么 如果您要编译/运行以下完整工作代码:C++ 为什么虚拟关键词是;持久的;在C++;?,c++,inheritance,C++,Inheritance,假设我有一个函数print(),分为3个类a、B和C。 C继承自B继承自A。 关键字virtual仅用于A 为什么下面两个都在C中使用print() 这里的直觉是什么 如果您要编译/运行以下完整工作代码: #include <iostream> #include <cstdlib> using namespace std; class A{ public: A(){ cout << "construct A" << en
#include <iostream>
#include <cstdlib>
using namespace std;
class A{
public:
A(){
cout << "construct A" << endl;
}
virtual void print(){
cout << "A says" << endl;
}
};
class B: public A{
public:
B(){
cout << "construct B" << endl;
}
void print(){
cout << "B says" << endl;
}
};
class C: public B{
public:
C(){
cout << "construct C" << endl;
}
void print(){
cout << "C says" << endl;
}
void print(int x){
cout << "C says " << x << endl;
}
};
int main(){
A* ac = new C();
ac->print();
B* bc = new C();
bc->print();
return 0;
}
#包括
#包括
使用名称空间std;
甲级{
公众:
(){
CUT< P>因为C++是这样工作的——一旦一个特定签名的函数被标记为“代码>虚拟< /代码>,无论每个派生类是否仍然使用关键字,都仍然是代码>虚拟<代码>。我的首选是总是使用额外的(冗余)。virtual
来明确发生了什么,但其他人认为不需要它,所以您不妨将其忽略
(但是,具有不同签名的重载不会自动是虚拟的,实际上会隐藏同名的基类方法,除非使用using
指令将基类定义引入派生范围。)
至于原因,我认为没有任何特定的语言理由禁止您“去虚拟化”一个函数。我猜Stroustrup在早期决定原谅人们在派生类中忘记了virtual
,并因此导致意外行为。该语言可以以任何一种方式工作-这是Stroustrup做出的选择:
- 实际上,在不同的派生深度上重写select函数很容易,而无需在每个中间类中重新列出所有未重写的函数。随着代码的发展,这减少了维护负担,当您查看派生类时,它强调的是实际更改或添加的内容,而不太混乱
- 如果它像您期望的那样工作,并且您必须在
B
中再次显式声明虚拟函数,那么编译器将期望实现,并且链接器将失败,因此需要一些新的符号来指示您只保留进一步派生类重写函数B的能力ut不打算替换基类定义。这听起来有点乏味和重复
默认使用覆盖虚拟函数的持续功能有助于重用,但对于一些相对罕见的情况,例如公司库API内部允许一些覆盖,但外部修改被故意阻止,那么在C++11中有一个final
关键字that表示不允许派生类重写该函数:请参见它必须重写。它是基类表示的契约的一部分。如果它没有传播到派生类,则将违反OO的原则,例如Liskov替换原则。oops打字错误。我将修复。B::print也不会使用C::print。什么方法d您希望/希望它使用吗?铁杆人物也会有一点作弊,并使提供所谓“向下钻取行为”的->
操作员过载这是另一种多态性,但我不知道它是如何实用的,通常是C++编译器用这种技术显示错误的迹象。@ USE248510EEEEK,听起来有点讨厌。我只见过“智能代码指针和迭代器”的代码< >运算符> <代码>重载。(这是合理的,因为它们都像指针一样工作)。你有这种深入行为的例子吗?如果有人关心的话:在C中,你必须在保持虚拟化(关键字覆盖)或反虚拟化功能(关键字新建)之间做出选择.在Java中,任何东西都是虚拟的。@user2485710:我不知道你的建议,但我仍然相当肯定“编译器错误”实际上是“未定义的Bheavior”的例子,即不是编译器中的错误。从什么意义上讲,它会违反LSP?我不同意这一点。另一种方法是要求用户为派生类显式提供virtual
关键字。不会违反LSP。
#include <iostream>
#include <cstdlib>
using namespace std;
class A{
public:
A(){
cout << "construct A" << endl;
}
virtual void print(){
cout << "A says" << endl;
}
};
class B: public A{
public:
B(){
cout << "construct B" << endl;
}
void print(){
cout << "B says" << endl;
}
};
class C: public B{
public:
C(){
cout << "construct C" << endl;
}
void print(){
cout << "C says" << endl;
}
void print(int x){
cout << "C says " << x << endl;
}
};
int main(){
A* ac = new C();
ac->print();
B* bc = new C();
bc->print();
return 0;
}