c++inheritanceoverridingvtablename-hiding

Who's function get called when calling f1() through Derived::f2()?


#include <iostream>
#include <string>
using namespace std;

class Base {
public:
 Base(const string& s): str(s) {cout<<"Base::ctor\n";}
 Base(const Base& b): str(b.str) {cout<<"Base::copy ctor\n";}
 virtual ~Base() {cout<<"Base::dtor\n";}
 void f1() {cout<<"Base::f1()\n"; f2();} //2 orders
 virtual void f2() {cout<<"Base::f2()\n";}

private:
 string str;
};

class Derived : public Base {
public:
 Derived(const string& s): Base(s)
 {cout<<"Derived::ctor\n";}
 Derived(const Derived& d): Base(d)
 {cout<<"Derived::copy ctor\n";}
 ~Derived() {cout<<"Derived::dtor\n";} 
 virtual void f1() {cout<<"Derived::f1()\n"; f2();}
 void f2() {cout<<"Derived::f2()\n"; f1();} //jumps from here to Leaf's f1()
};

class Leaf : public Derived {
public:
 Leaf(const string& s): Derived(s)
 {cout<<"Leaf::ctor\n";}
 Leaf(const Leaf& dd): Derived(dd)
 {cout<<"Leaf::copy ctor\n";}
 ~Leaf() {cout<<"Leaf::dtor\n";}
 void f1() {cout<<"Leaf::f1()\n"; f3();}
 void f3() {cout<<"Leaf::f3()\n";}
};


int main() {
 Leaf * p = new Leaf("Hello");
 Base * p2 = new Leaf(*p);

 p2->f1();

 delete p2;
 delete p;
 return 0; 
}

Hello,

This question is an exam phrased one but it's very hard for me to find the right way to describe it and look for it online.

in line :

p2->f1(); 

the output is:

 Base::f1()
 Derived::f2()
 Leaf::f1()
 Leaf::f3()

in Derived f2() there's a call for f1(). who's going to be called? f1() of the type Base or f1() of Leaf? From what I have been taught, the compiler always looks for the function in the type to the left. (Base* p2 = new Leaf(*p) ) But over here I can see that it goes to f1() of class Leaf. I can see that it is Leaf's but don't understand why...

thanks for the helpers !


Solution

  • To answer your question quickly: Derived::f1() is called in Derived::f2().

    To understand why it's Derived::f1() that's called, you probably need the knowledge of "C++ name hiding in inheritance", which you can refer to some online articles like:

    You also need the knowledge of the "Unqualified name lookup" which you can refer to the "Member function definition" section in this web page: Unqualified name lookup.

    In summary, the major points are:

    The process of the call should be as follows:

    1. In the main(), p2->f1(); is executed.
    2. Because p2 is a pointer to Base, the name of "f1" is searched in Base's method list.
    3. Note that Base::f1() is NOT virtual, so Base::f1() is called ("Base::f1()"). Yes, f1() is declared as virtual in Derived, but this doesn't affect the virtual table of Base.
    4. Base::f1() calls f2 which is a virtual method of Base. Because f2 is only overridden in Derived, Derived::f2() is the one that's actually called("Derived::f2()").
    5. Derived::f2() calls f1() which is actually Derived::f1(). Because Derived::f1() is declared as virtual and overridden in Leaf, it is Leaf::f1() that is eventually called("Leaf::f1()").
    6. Leaf::f1() calls f3() which is Leaf::f3(). f3() is a method that only Leaf has so it is just called("Leaf::f3()").