Show Lecture.VirtualFunctions as a slide show.
class Base { public: void foo() { cout << "β\n"; } }; class Derived : public Base { public: void foo() { cout << "Δ\n"; } }; Derived d; Base *b = &d; b->foo();
β
b->foo()
called the .foo()
method of Base
. Why?
Because b
is a Base *
.
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.
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.
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!
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!
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:
There is, of course, a cost to all of this.
class Sherlock { public: void foo() { } char c; }; cout << sizeof(Sherlock) << '\n';
1
class Watson { public: virtual void foo() { } char c; }; cout << sizeof(Watson) << '\n';
16
Watson has a virtual function, so each object has an additional pointer. Then there’s alignment.
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 Δ
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.
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'
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.
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.