Search code examples
c++polymorphismusingvirtual-functions

Virtual function pitfalls and using base functions


I have been coding up a reference thing for virtual functions since I always forget some piece of how they work. Here is what I have thus far:

#include <iostream>

using namespace std;

struct Base
{
    virtual void foo(int one = 1, int two = 2) = 0;

    virtual Base* bar() { cout << "Base bar\n"; return this; }

    virtual void baz(Base*) { cout << "Base baz\n"; }

    virtual void boo(Base*) { cout << "Base boo\n"; }

    // Error: templates may not be virtual
    //template <typename T> virtual T bad(T t) {return t}

    virtual ~Base() {}
};

struct Derived : public Base
{
    void foo(int one = 3, int two = 4) 
        { cout << "one: " << one << " two: " << two << endl; }

    Derived* bar() { cout << "Derived bar\n"; return this; }

    void baz(Derived*) { cout << "Derived baz\n"; }

    using Base::boo;
    void boo(Derived*) { cout << "Derived boo\n"; }
};

void example1()
{
    Base*       pB = new Derived();
    Derived*    pD = new Derived();

    // Foo is called with default parameters based on pointer
    pB->foo();  // one: 1 two: 2
    pD->foo();  // one: 3 two: 4

    // Bar is overridden because return type can be implicitly converted
    pB->bar();  // Derived bar
    pD->bar();  // Derived bar

    // Baz is not overridden because parameters differ
    pB->baz(pB);    // Base baz
    pB->baz(pD);    // Base baz
    //pD->baz(pB);  // invalid conversion from Base* to Derived*
    pD->baz(pD);    // Derived baz

    // Boo using test
    pB->boo(pB);    // Base boo
    pB->boo(pD);    // Base boo
    pD->boo(pB);    // Base boo
    pD->boo(pD);    // Derived boo

    delete pB;
    delete pD;
}

struct Base2
{
    void foo(int one = 1, int two = 2) { foo_impl(one, two); }

    virtual ~Base2() {}

private:
    virtual void foo_impl(int one, int two) = 0;
};

struct Derived2 : public Base2
{
private:
    void foo_impl(int one, int two) 
        { cout << "one: " << one << " two: " << two << endl; }
};

void example2()
{
    Base2*       pB = new Derived2();
    Derived2*    pD = new Derived2();

    // Now one set of default parameters exists
    pB->foo();  // one: 1 two: 2
    pD->foo();  // one: 1 two: 2

    delete pB;
    delete pD;
}

int main()
{
   example1();
   example2();
   return 0;
}

I am confused by the boo() function. It seems to me that pB->boo(pD) near the end of example1() should call the overridden version of the function, but it calls the base version. Why? If you could explain where all using is needed and how it works, that would be helpful, thanks.

Also, if you can think of other pitfalls to watch for when using virtual functions, feel free to post those too. Thanks.


Solution

  • The answer can be see by what happens with the baz function. As you note in your comment, the derived class's baz does not override the base class's version because the parameters differ. That's the same case for boo.

    Your using Base::boo; statement doesn't in any way change boo not overriding. Instead it just brings the base version in scope for the derived class. Essentially, it's as if the derived class then has two overloads of the function.

    However with pB->boo(pD); overloads aren't relevant since the pointer is typed in terms of Base. So the compiler only knows about the base class's boo function. And thus since nothing is overriding boo it ends up calling the base version.