Derived Classes (C++)

Derived Classes
With inheritance, an object of a derived class can be treated as an object of its base class. For example, a program can create an array of base-class pointers that point to objects of many derived-class types. Despite the fact that the derived-class objects are of different types, the compiler allows this because each derived-class object is an object of its base class. However, we cannot treat a base-class object as an object of any of its derived class.

In general, if a class **Derived** has a public base class **Base**, then a Derived* can be assigned to a variable of type Base* without the use of explicit type conversion. The opposite conversion, from Base* to Derived*, must be explicit. Also, a pointer of a base class can point to an object of a class on a lower level. The opposite is not allowed.

A Manager is an Employee, so a Manager* can be used as a Employee*. However, an Employee is not necessarily a Manager, so an Employee* cannot be used as a Manager*. For example:

A  member is like a public member to a member of a derived class, yet it is like a private member to other functions. Typically, the cleanest solution is for the derived class to use only the public members of its base class.

Constructors and Destructors
If a base class has constructors, then a constructor must be invoked. Default constructors can be invoked implicitly. However, if all constructors for a base require arguments, then a constructor for that base must be explicitly called. Consider:

Arguments for the base class' constructor are specified in the definition of a derived class' constructor. In this respect, the base class acts exactly like a member of the derived class. For example:

A derived class constructor can specify initializers for its own members and immediate bases only. It cannot directly initialize members of a base.

Class objects are constructed from the bottom up: first the base, then the members, and then the derived class itself. They are destroyed in the opposite order: first the derived class itself, then the members, and then the base. Members and bases are constructed in order of declaration in the class and destroyed in the reverse order.

Always make base classes' destructors virtual when they're meant to be manipulated polymorphically.

Since Base's destructor is not virtual, it's ~Base that is called and not ~Derived. Therefore, all the important cleanup is not performed, which can create several resource leaks.

Copying
Copying of class objects is defined by the copy constructor and assignments. Consider:

Because the Employee copy fucntions do not know anything about Managers, only the Employee part of a Manager is copied. This is commonly referred to as //slicing// and can be a source of surprises and errors. One reason to pass pointers and references to objects of classes in a hierarchy is to avoid slicing. Other reasons are to preserve polymorphic behavior and to gain efficiency.

Polymorphism
Polymorphism enables you to deal in generalities and let the execution-time environment concern itself with the specifics. You can direct a variety of objects to behave in manners appropriate to those objects without even knowing their types - as long as those objects belong to the same inheritance hierarchy and are being accessed off a common base-class pointer or a common base-class reference.

Polymorphism promotes extensibility: Software written to invoke polymorphic behavior is written independently of the types of the objects to which messages are sent. Thus, new types of objects that can respond to existing messages can be incorporated into such as system without modifying the base system. Only client code that instantiates new objects must be modified to accomodate new types.

Virtual Functions
The invoked functionality in an object depends on the type of the pointer (or reference) used to invoke the function, not the type of the object for which the member functions is called. That is, the type of the handle determines which class's functionality to invoke.

With virtual functions, the type of the object, not the type of the handle used to invoke the member function, determines which version of a virtual function to invoke.

Virtual functions allow the programmer to declare functions in a base class that can be redefined in each derived class. The compiler and loader will guarantee the correct correspondence between objects and the functions applied to them.

Once a function is declared virtual, it remains virtual all the way down the inheritance hierarchy from that point, even if that function is not explicitly declared virtual when a derived class overrides it. Even so, it is a best practice to declare these functions virtual at every level of the class hierarchy to promote program clarity.

Abstract Classes
Abstract base classes are too generic to define real objects. Concrete classes provide the specifics that make it reasonable to instantiate objects. For example, a Shape makes sense only as the base of some class derived from it.

A class is made abstract by declaring one or more of its virtual functions to be "pure", by using the initializer = 0. No objects of that abstract class can be created. It can be used only as an interface and as a base for other classes. For example:

A pure virtual function that is not defined in a derived class remains a pure virtual function, so the derived class is also an abstract class. This allows us to build implementations in stages:

An important use of abstract classes is to provide an interface without exposing any implementation details. For example, an operating system might hide the details of its device drivers behind an abstract class. We can then specify drivers as classes derived from this abstract class, and manipulate a variety of drivers through that interface.

With the introduction of abstract classes, we have the basic facilities for writing a complete program in a modular fashion using classes as building blocks.