重载和重写是如何一起工作的



我了解重载和重写的基本知识-但是有些东西让我感到困惑。我将尝试用一个简单的例子来解释:

  • 类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::XD::X的参数列表不同,则认为B::XD::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&)),一旦完成,找到所选函数的正确实现。前者发生在编译时,取决于对象及其参数的静态类型,后者发生在运行时,取决于对象的动态类型(注意,并不取决于参数的动态类型)。

四个对象的声明如下:d1d2D&,因此它们的静态类型D&, b1b2B&,因此它们的静态类型为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&)相同,因为对象的动态类型与静态类型相同。

相关内容

  • 没有找到相关文章

最新更新