1

我了解重载和覆盖的基础知识——但有些事情让我感到困惑。我将尝试用一个简单的例子来解释:

  • B 类具有函数 X(B& b)
  • D类继承自B类。
  • D 类用 X(B& b) 覆盖 X 并用 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() 的哪些实现以及正在发生的一般机制是什么。

4

2 回答 2

2

X您在 中声明一个虚函数,并在派生类中B(B::X)覆盖。如果和的参数列表不同,并且被认为不同,则不会覆盖,并且不是虚拟的(除非您已使用 virtual 关键字声明它)。相反,隐藏.XD(D::X)B::XD::XB::XD::XD::XB::XD::XD::XB::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. 所以d1.X(d2)d1.X(b2)intest()无关B::X。和b1.X(d2),b1.X(b2)中将test()调用D::X。虽然B::Xd 在 D 中是不可见的,但D::X(B&)它是虚拟的,不管你是否D::X(B&)用 virtual 关键字声明。编译器知道它是一个虚函数,所以D::X(B&)被调用。

编辑:关于 b1.X(b2) 的更多解释,B::X 是一个虚函数,并且 D::X 覆盖它,所以它肯定会通过动态绑定调用 D::X。并且重载是在编译时确定的,所以它不会调用 D::X(D&)。

于 2013-03-03T02:39:26.520 回答
1

涉及两个步骤:重载选择(X(B&)vs. X(D&)),一旦完成,找到所选函数的正确实现。前者发生在编译时,取决于对象的静态类型及其参数,后者发生在运行时,取决于对象的动态类型(注意它不依赖于参数的动态类型)。

四个对象声明如下:d1and d2are D&,所以它们的静态类型D&, and b1andb2被声明为B&,所以它们的静态类型是B&。静态类型是您在代码中声明的类型。

但是所有四个的动态类型都是D,因为所有四个引用实际上都引用了您作为D-objects 在中创建的对象main()

因此,第一步,重载选择: 在 and 的情况下b1.X(b2)b1.X(d2)只有一个可能的重载,X(B&),因为静态类型是B&,而 for 的类定义B只有这一个函数。但是在 and 的情况下d1.X(b2)d1.X(d2)重载选择是基于 的类定义的D,因为静态类型是D&。因此考虑了两个重载:X(B&)X(D&). 当参数为b2时,选择前一个重载,当参数为 时d2,选择后一个重载——所有这些都基于对象和参数的静态(=声明)类型。

第二步,选择重载的正确实现。这发生在运行时,取决于对象的动态类型(而不是参数)。所以在的情况下b1.X(b2),因为是动态类型b1D所以最终会调用D::X(B&)。同样适用b1.X(d2):在上一步中选择的重载是X(B&),但选择的实现是D::X(B&)。(D::X(D&)此时不是候选者,因为那将是不同的重载,并且已经根据静态类型选择了重载)。在 and 的情况下d1.X(b2)d1.X(d2)选择的函数和第一步一样,D::X(B&)and D::X(D&),因为对象的动态类型和静态类型是一样的。

于 2013-03-03T03:21:34.390 回答