我了解重载和重写的基本知识-但是有些东西让我感到困惑。我将尝试用一个简单的例子来解释:
- 类B有一个函数X(B&b)
- D类继承b类
- 类D用X(B&b),也用X(D&d)。
然后我有以下代码:
void test(D& d1, B& b1, D& d2, B& b2){
d1.X(d2);
d1.X(b2);
b1.X(d2);
b1.X(b2);
}
int main(){
D d1, d2, d3, d4;
test(d1, d2, d3, d4);
}
我非常不确定test()
的第三行和四行将如何确定调用X()的哪些实现以及正在发生的一般机制。
在B(B::X)
中声明虚函数X
,并在派生类D(D::X)
中覆盖X
。如果B::X
和D::X
的参数列表不同,则认为B::X
和D::X
不同,D::X
不覆盖B::X
, D::X
不是虚的(除非您用virtual关键字声明了它)。相反,D::X
隐藏了B::X
。
#include <iostream>
using namespace std;
struct B {
virtual void X() { cout << "Class B" << endl; }
};
struct D: B {
void X(int) { cout << "Class D" << endl; }
};
int main() {
D d;
B* pb = &d;
// d.X();
pb->X();
}
你甚至不能调用d.X()
,它被D::X(int)
隐藏了。但pb->X()
是好的。
所以在你的例子中:
struct B {
virtual void X(B& b) { cout << "Class B" << endl; }
};
struct D: B {
void X(B& b) { cout << "Class D" << endl; }
void X(D& d) { cout << "Class D" << endl; }
};
D::X
将隐藏B::X
。所以test()
中的d1.X(d2)
和d1.X(b2)
与B::X
没有关系。test()
中的b1.X(d2)
和b1.X(b2)
将调用D::X
。虽然B::X
在D中是不可见的,但是D::X(B&)
是虚的,不管你是否用virtual关键字声明D::X(B&)
。编译器知道它是一个虚函数,所以调用D::X(B&)
。
编辑:关于b1.X(b2)的更多解释,B::X是一个虚函数,D::X覆盖它,所以它肯定会通过动态绑定调用D::X。重载是在编译时确定的,所以它不会调用D::X(D&)。
有两个步骤:重载选择(X(B&)
vs. X(D&)
),一旦完成,找到所选函数的正确实现。前者发生在编译时,取决于对象及其参数的静态类型,后者发生在运行时,取决于对象的动态类型(注意,并不取决于参数的动态类型)。
四个对象的声明如下:d1
和d2
为D&
,因此它们的静态类型为D&
, b1
和b2
为B&
,因此它们的静态类型为B&
。静态类型是您在代码中声明的类型。
但是这四个动态类型都是D
,因为所有四个引用实际上都是引用您创建的D
- main()
中的对象。
因此,第一步,重载选择:在b1.X(b2)
和b1.X(d2)
的情况下,只有一个可能的重载X(B&)
,因为静态类型是B&
, B
的类定义只有这个函数。但是在d1.X(b2)
和d1.X(d2)
的情况下,重载选择是基于D
的类定义,因为静态类型是D&
。因此考虑两个重载:X(B&)
和X(D&)
。当实参为b2
时,选择前一个重载;当实参为d2
时,选择后一个重载–所有这些都基于对象和参数的静态(=声明)类型。
第二步,选择所选重载的正确实现。这发生在运行时,取决于对象的动态类型(而不是参数)。所以在b1.X(b2)
的情况下,因为b1
的动态类型是D
,它最终会调用D::X(B&)
。b1.X(d2)
也一样:在上一步中选择的重载是X(B&)
,但是选择的实现是D::X(B&)
。(D::X(D&)
在这一点上不是候选的,因为那将是一个不同的重载,重载已经根据静态类型选择了)。对于d1.X(b2)
和d1.X(d2)
,所选择的函数与第一步D::X(B&)
和D::X(D&)
相同,因为对象的动态类型与静态类型相同。