Polymorphic Usage Of Objects

What Larger Systems Want

What we have

  • Automatic conversion from Derived to Base

  • virtual ⟶ call Derived::method() through Base*

What we want

  • Have multiple objects of different concrete types (say, Derived1, Derived2) in a system

  • Access them through their base types

  • interfaces (but there are gotchas - see C++ “Interfaces”)

  • polymorphic usage of objects

Why do we want this?

  • System must not depend on one concrete type out of many

../../../../../_images/polymorphic.jpg
#include <iostream>


class Base
{
public:
    virtual void method() const
    {
        std::cout << "Base::method()" << std::endl;
    }
};

class Derived1 : public Base
{
public:
    virtual void method() const
    {
        std::cout << "Derived1::method()" << std::endl;
    }
};

class Derived2 : public Base
{
public:
    virtual void method() const
    {
        std::cout << "Derived2::method()" << std::endl;
    }
};


int main()
{
    Base* thermometers[2];

    Derived1 d1;
    Derived2 d2;

    thermometers[0] = &d1;
    thermometers[1] = &d2;

    for (int i=0; i<2; i++)
        thermometers[i]->method();

    return 0;
}
$ ./inher-oo-polymorphic
Derived1::method()
Derived2::method()

Pitfall ⟶ Pure Virtual Methods

Problem

  • Base::method() is there (has an implementation that can be invoked)

  • Used when a derived class does not override it

  • ⟶ Countless source of errors; e.g.

    • Developer goes out for coffee before implementing Derived3::method()

    • Comes back, not knowing where she left off

    • Everything compiles ⟶ false expectation of correctness

    • Base::method() used ⟶ Bug!!

#include <iostream>


class Base
{
public:
    virtual void method() const
    {
        std::cout << "Base::method()" << std::endl;
    }
};

class Derived1 : public Base
{
public:
    virtual void method() const
    {
        std::cout << "Derived1::method()" << std::endl;
    }
};

class Derived2 : public Base
{
public:
    virtual void method() const
    {
        std::cout << "Derived2::method()" << std::endl;
    }
};

class Derived3 : public Base          // <--- BUG: not implementing method()
{
};


int main()
{
    Base* thermometers[3];

    Derived1 d1;
    Derived2 d2;
    Derived3 d3;                      // <--- BUG: instantiating incomplete type

    thermometers[0] = &d1;
    thermometers[1] = &d2;
    thermometers[2] = &d3;            // <--- BUG: using incomplete type

    for (int i=0; i<3; i++)
        thermometers[i]->method();

    return 0;
}
$ ./inher-oo-polymorphic-not-pure
Derived1::method()
Derived2::method()
Base::method()                # <--- BUG!

Pure Virtual Method

Solution

  • Pure virtual method

  • virtual Base::method() const = 0;

#include <iostream>


class Base
{
public:
    virtual void method() const = 0;     // <--- purposely left unimplemented!!
};

class Derived1 : public Base
{
public:
    virtual void method() const
    {
        std::cout << "Derived1::method()" << std::endl;
    }
};

class Derived2 : public Base
{
public:
    virtual void method() const
    {
        std::cout << "Derived2::method()" << std::endl;
    }
};

class Derived3 : public Base             // <--- ERROR: not implementing method()
{
};


int main()
{
    Base* thermometers[3];

    Derived1 d1;
    Derived2 d2;
    Derived3 d3;

    thermometers[0] = &d1;
    thermometers[1] = &d2;
    thermometers[2] = &d3;

    for (int i=0; i<3; i++)
        thermometers[i]->method();

    return 0;
}
inher-oo-polymorphic-pure.cpp:39:14: error: cannot declare variable ‘d3’ to be of abstract type ‘Derived3’
   39 |     Derived3 d3;
      |              ^~
inher-oo-polymorphic-pure.cpp:28:7: note:   because the following virtual functions are pure within ‘Derived3’:
   28 | class Derived3 : public Base
      |       ^~~~~~~~
inher-oo-polymorphic-pure.cpp:7:18: note:     ‘virtual void Base::method() const’
    7 |     virtual void method() const = 0;
      |                  ^~~~~~