与C++中的虚拟关键字混淆

Confusion with virtual keyword in C++

本文关键字:关键字 虚拟 C++      更新时间:2023-10-16

我正在研究virtual关键字在C++的效果,我想出了这个代码。

#include<iostream>
using namespace std;
class A {
public:
virtual void show(){
cout << "A n";
}
};
class B : public A {
public:
void show(){
cout << "B n";
}
};
class C : public B {
public: 
void show(){
cout << "C n"; 
}
};
int main(){
A *ab = new B;
A *ac = new C;
B *bc = new C;
ab->show();
ac->show();
bc->show();
}

预期输出为:

B
C
B

由于 B 中的show函数是非虚拟的。但是编译时的结果是:

B
C
C

它的行为就好像 B 中的show函数是虚拟的。为什么会这样呢?B类在这里被覆盖了吗?如果我将 C 类指向 B 类,为什么我会指向 A 类?

根据 2017 C++标准(10.1.2 函数说明符)

2 虚拟说明符只能在初始声明中使用 非静态类成员函数;参见 13.3.

和(13.3 虚拟函数)

2如果虚成员函数 vf 在类 Base 和 一个类 派生的,直接或间接派生自 Base,一个成员 同名函数 VF,参数类型列表 (11.3.5), cv-qualifion,和ref-qualifier(或没有相同)作为Base::vf 被声明,则派生::vf 也是虚拟的(无论是否如此) 声明),它覆盖 111 Base::vf。为了方便起见,我们说 任何虚拟函数都会覆盖自身。虚拟成员函数 C::vf 类对象 S 是最终覆盖器,除非派生最多的类 (4.5) 其中 S 是基类子对象(如果有的话)声明或 继承另一个重写 VF 的成员函数。在派生中 类,如果基类子对象的虚拟成员函数具有更多 而不是一个最终的覆盖程序,该程序格式不正确。

因此,类Bshow的函数是虚函数,因为它与类A中声明的函数具有相同的签名。

考虑一个更有趣的示例,当在类B中添加了限定符const到成员函数show

#include<iostream>
using namespace std;
class A {
public:
virtual void show(){
cout << "A n";
}
};
class B : public A {
public:
void show() const{
cout << "B n";
}
};
class C : public B {
public: 
void show() {
cout << "C n"; 
}
};
int main(){
A *ab = new B;
A *ac = new C;
B *bc = new C;
ab->show();
ac->show();
bc->show();
}

在这种情况下,输出将如下所示

A 
C 
B 

在此表达式语句中

ab->show();

在类A中声明show称为虚函数。

在此声明中

ac->show();

在类C中调用了相同的虚函数。编译器使用类 A 中的虚函数声明,因为指针ac的静态类型是A *

在此声明中

bc->show();

有称为非虚拟成员函数show限定符const,因为指针bc的静态类型是B *,编译器在类B中找到隐藏类A中声明的虚函数的函数。

对于原始程序,您可以使用说明符override使类定义更加清晰。例如

#include<iostream>
using namespace std;
class A {
public:
virtual void show(){
cout << "A n";
}
};
class B : public A {
public:
void show() override{
cout << "B n";
}
};
class C : public B {
public: 
void show() override{
cout << "C n"; 
}
};
int main(){
A *ab = new B;
A *ac = new C;
B *bc = new C;
ab->show();
ac->show();
bc->show();
}

如果在base类中指定,则无需将函数指定为派生类中的virtual函数。

行为是正确的。由于show函数是虚拟的,因此调用的版本将是附加到要调用它的实例的版本,而不是由该实例的类型(可以是该实例的实际类型的基础)描述的版本。