Thinking in C++ Notes (15)

Chapter 15: Polymorphism & Virtual functions

  • 3 essential feature of OOP: data abstraction, inheritance and polymorphism
  • early binding: function call binding is performed before the program is run
    late binding(dynamic binding or runtime binding): function call binding occurs at runtime
  • if the function is declared as virtual in the base class, it is virtual in all the derived classes
    only the declaration needs the virtual keyword, not the definition
  • when a virtual function is called, the "closest" definition in the inheritance hierarchy is automatically used

How C++ implements late binding

  • typical compiler creates a VTABLE for each class that contain virtual functions. Addresses of the virtual member functions is placed in the VTABLE. In objects that contains virtual functions, a pointer pointed to VTABLE(called vpointer) is placed at the beginning of the object.
  • when you call the virtual function, compiler automatically inserts code to fetch the address of the function in VTABLE through vpointer.
  • Initialization of VPTR happens in the constructor
  • if you don’t override a function that was declared virtual in the base class, the compiler uses the address of the base-class version in the derived class
  • compiler ensures that all the function pointers in all the VTABLEs of a particular class hierarchy occur in the same order
  • VPTR is inserted at the beginning of the object, so the contents of “this” correspond to the VPTR
  • For efficiency’s sake, most compilers will perform early binding when they are making a call to a virtual function for an object(not a pointer or reference) because they know the exact type
  • abstract class: a class with at least a pure virtual function.
    you cannot create an object of an abstract class
  • when an abstract class is inherited, all pure virtual functions must be implemented, or the inherited class becomes abstract as well
  • you create an abstract class when you only want to manipulate a set of classes through a common interface, but the common interface doesn’t need to have an implementation( at least a full implementation)
  • inline pure virtual definitions are illegal
  • if you upcast to an object instead of a pointer or reference, the object is “sliced”
  • when passing by value, the copy-constructor for a base-class object is used, which initializes the VPTR to the base-class VTABLE and copies only the base-class part of the object (P657)

overloading & overriding

  • the compiler will not allow you to change the return type of an overridden function( it will allow it if f() is not virtual)
    This is an important restriction because the compiler must guarantee that you can polymorphically call the function through the base class
  • if you override one of the overloaded member functions in the base class, the other overloaded versions become hidden in the derived class
    if you upcast derived-class to base-class, then only  the base-class versions are available and the derived-class version is not available (P660)
  • if you’re returning a pointer or a reference to a base class, then the overridden version of the function may return a pointer or reference to a class derived from what the base returns (P660)

  • compiler will call the default constructor if you don’t explicitly call a base-class constructor in the constructor initializer list
  • when you’re in the derived-class constructor, all the members you in access in the base class have been initialized
  • if you call a virtual function inside a constructor, only the local version of the function is used
    • the constructor’s job is to bring the object into existence
    • when a constructor is called, one of the first things it does is initialize its VPTR. The state of the VPTR is determined by the constructor that is called last
  • you cannot use the virtual keyword with constructors, but destructors can and often must be virtual
  • constructors and destructors are the only places where the hierarchy of calls must happen
  • forgetting to make a destructor virtual is an insidious bug because it often doesn’t directly affect the behavior of your program, but it can quietly introduce a memory leak
  • it is possible for the destructor to be virtual because its VPTR is initialized, so virtual function calls can take place
  • pure virtual destructors are legal in Standard C++, and you must provide a function body for the pure virtual destructor
  • Unlike every other pure virtual function, you are not required to provide a definition of a pure virtual destructor in the derived class
  • as a guideline, any time you have a virtual function in a class, you should immediately add a virtual destructor
  • inside a destructor, only the “local” version of the member function is called; the virtual mechanism is ignored
  • a virtual function is only capable of making a single dispatch(determining the type of one unknown object). To determine both types a technique called multiple dispatching is used.(P678)
  • downcasting