对C++多重继承中函数名称查找的理解

    科技2024-11-25  30

    class A1 { public: virtual void P() const = 0; void print() const { std::cout << "A1\n"; }; }; class B1 : public A1 { public: void P() const override final { print(); } void print() const { std::cout << "B1\n"; } }; class B2 : public B1 { public: virtual void print() const { std::cout << "B2\n"; } }; class C1 : public B2 { public: void print() const { std::cout << "C1\n"; } }; int main() { B1 b1; B2 b2; C1 c1; A1* a1 = &b1; A1* a2 = &b2; A1* a3 = &c1; B1* b11 = &c1; B2* b22 = &c1; a1->P(); // 1 a2->P(); // 2 a3->P(); // 3 b11->P(); // 4 b22->P(); // 5 return 0; }

    上述的输出全部都是"B1"。

    原因是表达式1在执行名称查找时,首先去A1类中查找(因为a1的类型显式类型是A1指针),发现P()是一个虚函数,因此就会从a1对象中的虚函数表中查找P()的函数指针,发现此函数指针指向的是B1::P()。然后B1::P()中调用了print(),因此直接在类B1中查找print(),如果找不到的话,编译器会继续到类B1作用域的上层,即类A1的作用域中查找print()名称(所以如果你把B1::print()注释掉的话,最终结果都是"A1"),编译器在B1中找到了print(),并发现它不是虚函数,因此不会去a1对象的虚函数表中查找print(),因此最终调用的就是B1::print();

    表达式2和3在执行名称查找时,跟表达式1一样,因为P()是虚函数,所以会从对应的对象的虚函数表中查找;

    表达式4执行名称查找时,会先从B1对象中查找名称P()(因为b11的显式类型是B1指针),发现P()是一个虚函数,因此会从对象b11的虚函数表中查找P()的地址。由于b11对应的实际类型C1并没有重载P(),因此内存中b11对象的虚函数表中P()的指针仍然是B1::P(),并没有被覆写。接着查找在B1::P()中看到了名字print(),并继续查找print(),且查找过程同上面的分析一致,所以最终调用的也是B1::print();

    表达式5看到P()时会先到类B2的作用域里面查找,发现找不到,因此就会扩大查找范围,到B2类的基类B1中查找,结果找到了。然后的分析过程就跟上面一样了,最终也是调用的B1::print()。 如果

    Processed: 0.010, SQL: 8