CS253: Software Development with C++

Spring 2023

Virtual Functions

Show Lecture.VirtualFunctions as a slide show.

CS253 Virtual Functions

Pointers

class Base {
  public:
    void foo() { cout << "β\n"; } // Base prints a beta
};

class Derived : public Base {
  public:
    void foo() { cout << "Δ\n"; } // Derived prints a delta
};

Derived d;
Base *b = &d;
b->foo();
β

b->foo() called the .foo() method of Base. Why? Because b is a Base *.

References

class Base {
  public:
    void foo() { cout << "β\n"; }
};

class Derived : public Base {
  public:
    void foo() { cout << "Δ\n"; }
};

Derived d;
Base &b = d;
b.foo();
β

Same result with a reference. References are just a variety of pointers, so that’s expected.

Unsurprising

These results are unsurprising. If your pointer is a Generic *, then p->method() calls a method of Generic.

But what if that’s not what you want? What if you have a pointer of type Generic *, but it’s really pointing to an object of type Specific? It can do that, if Specific is a subclass (derived class) of Generic. You might want p->method() to call the method of the object’s actual type, as opposed to its apparent type.

Virtual

class Base {
  public:
    virtual void foo() { cout << "β\n"; }
};

class Derived : public Base {
  public:
    virtual void foo() { cout << "Δ\n"; }
};

Derived d;
Base *b = &d;
b->foo();
Δ

That’s different!

Virtual

class Base {
  public:
    virtual void foo() { cout << "β\n"; }
};

class Derived : public Base {
  public:
    virtual void foo() { cout << "Δ\n"; }
};

Derived d;
Base &b = d;
b.foo();
Δ

That’s different!

Terminology

People often call these “virtual functions”, though a good O-O programmer might call them “virtual methods”. A method is a function, after all.

Not using virtual methods:

Using virtual methods:

Cost

There is, of course, a cost to all of this.

Sizes

class Sherlock {
  public:
    void foo() { }              // non-virtual
    char c;
};

class Watson {
  public:
    virtual void foo() { }      // virtual
    char c;
};
cout << sizeof(Sherlock) << ' ' << sizeof(Watson) << '\n';
1 16

Watson has a virtual function, so each object has an additional pointer. Then there’s alignment.

Virtual Dtor

class Base {
  public:
    virtual void foo() { cout << "β\n"; }
};

class Derived : public Base {
  public:
    virtual void foo() { cout << "Δ\n"; }
    string s;                   // Look--a data member!
};

Base *b = new Derived;
b->foo();
delete b;  // 🦡
c.cc:14: warning: deleting object of polymorphic class type ‘main()::Base’ 
   which has non-virtual destructor might cause undefined behavior
Δ

Contamination

If a method is declared virtual in the base class, it’s automatically virtual in all derived classes. It’s virtual all the way down.

However, I generally repeat the virtual declaration in each subclass to remind the reader.

Example

Here’s some ugly syntax. Since Super contains a pure virtual function, it cannot be instantiated. It is an Abstract Base Class, or ABC—similar to an interface in Java.

class Super {
  public:
    virtual void foo() = 0;
};

Super s;  // 🦡
c.cc:6: error: cannot declare variable ‘s’ to be of abstract type 
   ‘main()::Super’

Example

class Super {
  public:
    virtual void foo() = 0;
};

class Sub : public Super {
  public:
    void foo() {
        clog << "Aquaman or the Sub-Mariner?  You decide!\n"
             << "🐟 🐟 🐟 🐟 🐟 🐟 🐟 🐟 🐟 🐟 🐟 🐟 🐟 🐟 🐟\n";
    }
};

Sub s;
s.foo();
Aquaman or the Sub-Mariner?  You decide!
🐟 🐟 🐟 🐟 🐟 🐟 🐟 🐟 🐟 🐟 🐟 🐟 🐟 🐟 🐟

Sub has no pure virtual functions remaining, so it can be instantiated.

C++ ≠ Java

class Parent {
    public String who() { return "parent"; }
}
class Child extends Parent {
    public String who() { return "child"; }
}

public class Code {
    public static void main(String args[]) {
        Parent p = new Child();
        System.out.println(p.who());
    }
}
child

All Java methods act like C++ virtual methods.