Programming Language Concepts Using C and C++/Object Orientation and Inheritance in C++

Logic of inheritance does not change much from one programming language to another. For instance, if base and derived classes share the same public interface, the derived class is said to be a subtype of its base class and instances of it can be treated as instances of the base class. Or, thanks to dynamic dispatch, inheritance can be used to provide polymorphism.

However, newcomers to C++ from Java, or any other object-oriented programming language for that matter, are in for a few surprises. What we will do in this chapter is to take a look at inheritance in C++ and underline the differences with other programming languages.

Inheritance Kinds
First peculiarity of C++ is the assortment of inheritance kinds it offers to programmers: public, protected, and private. This meets the newcomer with the first inheritance example she tries. Take a look at the following code snippet.



This innocent-looking code claims to derive  from. However, it will not let you treat an object of  as an object of. That’s right:  is not seen as a subtype of. It looks like either C++ or the programmer got it wrong. Not exactly! Similar to the default section being, inheritance, unless otherwise stated, is taken to be of the so-called private kind. Deferring the answer of what we mean by private inheritance to another section, we modify the above code to meet our expectations as given below.



Done with this peculiarity, let’s move on to see some code examples. We will be using the following class definitions throughout the examples.

No Member Initialization
As a Java programmer you might think the above code fragment should produce two 0's in succession. However, it will output two random values. Unlike Java and C# where, unless overridden, data members are provided with default initial values, C++ compiler does not initialize the data members. If they need to be initialized to 0 or to any other value, this must be done explicitly by the programmer. It should also be noted that one cannot declare a data member with an initial value. That is, changing  to   in   will give rise to a syntactic error.


 * or

The C++ compiler has full confidence in the programmer. After all, a C++ programmer does not make a mistake. An error prone statement, which may be seen as the begetter of a hideous mistake in Java, is assumed to be the conscious decision of an all-knowing programmer. It’s not a bug it’s a feature!

Default Dispatch Type is Static Dispatch
As a competent Java programmer you would expect this to produce&mdash;depending on the value  evaluates to&mdash;  or. However, in C++ it always outputs !!! Unlike Java, C++, unless otherwise told, uses static dispatch in binding function calls. This means the address of the function invoked by the call to  will be resolved statically. That is, the compiler will use the static type of the identifier. In other words, functions invoked as a result of the calls made can be figured out by checking the program text.

A virtual function is dispatched dynamically. So, unlike the previous one, this example will compile and yield an output depending on the value of. If it evaluates to  it will output "In D::f1d(void)", otherwise it will output "In B::f1d(void)".

Hide-by-name Overloading
With Java semantics, above code outputs "In B::f2(int)". After all,  is also an object of type   and can use the public interface of   just like a genuine   object. So, both  and   are exposed to clients of. Not in C++! Unlike Java, where base and derived class member functions make up a set of overloaded functions, C++ restricts this set to a single scope. Since derived and base classes are different scopes, any derived class function with a name coinciding with a base class function will shadow all the functions in the base class. Technically, we say C++ hides by name while Java is said to hide by signature.

But doesn’t it go against the logic of inheritance? You claim  to be an object of   (through the public inheritance relationship) and don’t let its clients use some function appearing in the public interface of  ? That’s right and C++ provides means to meet your expectations.

Regardless of whether the call is to some virtual function or not, explicit use of the class name in the function call causes it to be dispatched statically. In the following function, for instance, [although  is of type   and   is  ] function call in the second statement will be dispatched to.

Note this type of static dispatch can be used to invoke any function in the receiver object’s class or any function in any one of the ancestor classes.

Multiple Inheritance, No Root Class
Unlike Java, where a class can derive from one and only one class, C++ supports derivation from multiple classes. Considered with the fact that interface notion is not supported, this feature is heavily used to implement interfaces.



One other point to take note of in C++ is its lack of a root class. That is, there is no class&mdash;such as the  class in Java&mdash;that serves as a common denominator among different classes. Consequently, one talks about a directed acyclic graph of classes instead of a tree of classes.

Test Program
Peculiarities.cxx

Inheritance a la Java
In this part of the handout, we provide an insight into how C++ and Java can be related as far as inheritance is concerned. This is accomplished by simulating the concepts found in Java using those found in C++. Such an approach should not be taken as an advertisement campaign of Java; needless to say Java is not without competition. It should rather be taken as an incomplete attempt at providing clues to the inner workings of the mentioned concepts.

Root Class and the Interface Concept
In Java, expressing common attributes among unrelated objects is made possible by means of the root class and the interface concept. The former defines a common denominator among all classes, while the latter is used to classify a group of classes. For instance, because it is listed in  all objects can be tested for equality with an object of a compatible type; or objects of classes claiming to be   can be compared with a compatible object.

These two notions are not supported in C++ as a linguistic abstraction. Instead, programmers are expected to resort to using conventions or simulate it through other constructs. For instance, testing for equality is accomplished by overriding the default implementation of the  operator; interface concept, which is not directly supported, can be simulated by means of abstract classes with pure virtual functions.

Object

Our intention in having the header file  is to define a root class that can be used as a polymorphic type in generic functions, such as   defined in  ; we do not mean to provide any shared functionality as is done in   class of Java. This, however, cannot be accomplished simply by defining an empty class. In order for a type to be polymorphic in C++ it must have at least one virtual function. We therefore include a dummy virtual function in our class definition.

But then, why did we make its access modifier ? First of all, it cannot be  because we don’t want any functionality to be exposed through this class. What about declaring no_op to be ? After all, declaring it as  means deriving classes can now send the   message. Answer lies in the nature of polymorphism: In order for polymorphism to be possible, one should be able to override the definition of a dynamically-dispatched function found in the base class. This implies such functions should be open at least to the derived classes. As a matter of fact C++ compilers will not even let you declare  functions in a   section.

Definition: A pure virtual function is a virtual function that is not given a function body in the declaring class. A derived class claiming to be concrete must therefore provide an implementation for such a function.

In terms of C++-supported concepts, an interface is a "fieldless" abstract class whose all functions are pure virtual.

IComparable

Interface (Rational)
Rational

Having defined the interface concept as a variation on the class concept, we naturally should be cautious about speaking of the implementation relation. This is indeed the case in C++: one can speak of the extension relation only. As a consequence support for multiple inheritance is a must.

Notice the following functions, unlike the rest of the member functions, will be dispatched statically. In Java, such an effect can be achieved by declaring methods to be.

In addition to marking functions, we also declare them to return a reference. This is because a reference is the best candidate for serving the purpose of handles in Java: it is an inheritance-aware, compiler-managed pointer. That is, we can pass as argument to the next function [or any function expecting a reference to, for that matter] an object belonging in the class hierarchy rooted by the   class; dereferencing of a reference is automatically done by the compiler-synthesized code.

As an alternative&mdash;although the resulting code would be less writable and readable&mdash;we could have used a plain pointer. However, using a plain object type is out of question. This is due to the fact that polymorphism together with inheritance requires sending the same message&mdash;that is what polymorphism is all about&mdash;to objects of probably varying sizes&mdash;enter inheritance&mdash;which in turn implies passing and returning variable-sized objects. This is something compilers cannot deal with! We should provide some assistance, which we do by injecting a fixed-size programming entity in between: pointer or reference.

Note the following function serves a purpose akin to that of  in Java. Replacing sstream instead of  and changing the implementation accordingly would make the analogy a more perfect one.

Implementation (Rational)
Rational.cxx

Note the missing  block! Unlike Java, C++ does not mandate the programmer to put all potentially problematic code in a guarded region. One can say all C++ exceptions are treated like the Java exceptions deriving from the  class. This gives the programmer a degree of freedom that enables her to come up with cleaner code. For instance, reaching the next line means we are adding two well-formed  objects. Result of such an action can never create a problem!

Now that we are done with the temporary object that holds the inverse of, we must return it to the memory allocator or put up with the consequences of creating garbage at each use of this function. That’s pretty annoying! But then again, a C/C++ programmer does not make such easy mistakes.

Notice the address-of operator before. Application of this operator to a reference returns the starting address of the region aliased by the reference. [Remember references are silently dereferenced at their point of use] In our case, this will be the address of the object created as a result of sending the message  to.

We formulate subtraction in terms of other operations: instead of subtracting a value, we add the negated value. For doing this we create two temporary objects meaningful only throughout the current call. Before returning to the caller we should return them to the memory allocator.

A so-called smart pointer object is exactly what we want. Such an object is initialized to point to a dynamically allocated object created by a new expression and frees it&mdash;the dynamically allocated object&mdash;at the end of its (smart pointer’s) lifetime. The following figure showing the memory layout after execution of line 47 should make this clear.



Heap object local to the function is created together with the smart pointer object, which is itself a local object created on the run-time stack.9 This means allocation-constructor call and destructor call-deallocation of this smart pointer object will be handled by the compiler-synthesized code. In other words, programmer need not worry about the life-cycle management of the smart pointer object. So, if we can guarantee the heap object is destroyed-deallocated together with this smart pointer, its life-cycle management will not be a problem anymore. This is accomplished by delet(e)ing the heap object within the destructor of the related smart pointer object, which means the heap object will have been destroyed-deallocated by the time the destruction of the smart pointer object is over. The following then describes the life-cycle of the smart pointer and the related heap object.


 * 1) Create the smart pointer in the run-time stack.
 * 2) Pass the related heap object to the constructor of the smart pointer.
 * 3) Use the heap object.
 * 4) Call the destructor of the smart pointer by means of the compiler-synthesized code.
 * 5) Delete the heap object from within the destructor of the smart pointer.

According to this, anonymous  objects&mdash;  and  &mdash;created in the following definitions will have been automatically&mdash;that is, without the intervention of the programmer&mdash;returned before leaving the function.

Observe the following application of the dereferencing operator receives as its operand a non-pointer variable, which may at first seem as an error. After all,  works by returning the contents of memory indicated by its sole operand. However, this rather limited description ignores the possibility of overloading the dereferencing operator. It is indeed the overloaded version of this operator that enables the use of a non-pointer type. The following application of  makes use of the overloaded version defined within the   class, which returns the contents of the heap object managed by the smart pointer.

To make things clearer, we can suggest the following implementation for the  class.

In addition to the traditional C-style casting C++ offers a variety of cast operators:,  ,  , and. Each of these performs a subset of the functionality offered by the traditional cast operator and therefore one can say the new operators do not add any new functionality. Nevertheless, thanks to the extra support from the compiler, they enable writing more type-safe programs. Using the new operators we state our intentions explicitly and therefore get more maintainable code.

Since our intention of removing the -ness has been made explicit by the relevant operator, maintainer of the code will more quickly spot the occurrence of cast and realize what is being done. Alternative scheme of using the C-style cast lacks these qualities: it is both difficult to find the location of the cast and figure out that -ness is being removed. Using  will also provide us with the benefit of safer code. This particular operator is used to bi-directionally cast between polymorphic classes&mdash;that is; classes that has at least one virtual function&mdash;that are related with each other by a public derivation.

Definition: Converting from one class to another in the same class hierarchy is referred to as downcasting if the target type is more specialized. In case the target type is less specialized the act of casting is called upcasting.

Upcasting to a public base class is always successful since the messages listed in the interface of the target type is a subset of the source type interface. On the other hand, casting from a derived class to one of its non-public base classes leads to a compile-time error. Similarly, downcasting may give rise to run-time errors since we can potentially send messages that are not found in the interface of the source type.

The above code downcasts a  variable to , through which one can send the extra message named. For this example, this doesn't seem to be a problem. But what if  was used to point to an object of   instead of  ? What if it is used to point to objects of different types as is shown in the following fragment?

There is no guarantee that we can send  to the underlying object, which can be of type   or. This guarantee we are seeking can be provided only if we can check the object type at run-time. And this is exactly what  does: by checking compatibility of the pointer/reference&mdash;static type&mdash;with the object&mdash;dynamic type&mdash;  decides whether the cast taking place is valid or not. If so a legitimate value is returned. Otherwise, in the case of casting a pointer, a  value is returned, which basically removes any possibility of sending an illegal message; in the case of failing to cast a reference   exception is thrown. Same actions are taken also when source and target types are not related with inheritance.

Note this cost due to the run-time check performed by the compiler-synthesized code is never experienced as a result of using the traditional cast operator. This is because the C-style cast operator makes no use of any run-time information.

Observe casting up the class hierarchy&mdash;since messages received by an object of the derived class is a subset of that of its base classes&mdash;does not need any run-time checks. This means cost due to  is not rationalized: why should we pay for making a control, whose result is known to us? Solution offered by C++ is another cast operator that does its job using compile-time information:. This operator can be used for performing conversions that are implicitly carried out by the compiler, performing these implicit conversions in the reverse direction. It can also be used in place of  if skipping the run-time checks is guaranteed to be safe.

This doesn't fully cover the functionality offered by the traditional cast operator. Conversions between unrelated/incompatible pointer types are missing, for instance. This missing functionality is covered by the, which can also perform conversions between pointer and integral types.

It should be kept in mind that this operator makes no checks on the source and target types; it simply bitwise-copies the contents of the target into the source.

Interface (Whole)
Whole

Remember  derives from. Put differently&mdash;since inheritance can be seen as a compiler-managed composition&mdash;all  objects have a   sub-object as part of their memory layouts. Following notation used in the member initialization list with no reference to the member being initialized will initialize the  sub-object found in the   object being constructed.

Implementation (Whole)
Whole.cxx

Exception Classes
NoInverse

ZeroDenominator

ZeroDivisor

Test Program
Test_Whole.cxx

Private Inheritance
Programming being a pragmatic endeavor, one must strive to do it as efficiently as possible. Arguably the most effective way to achieve this is to re-use artifacts that have already been used in different stages of previous projects. By doing so we save on development and test time, which means our next product makes it to the market in a shorter time.

One way to achieve reuse is inheritance. And for many it seems to be the only affordable one. Nevertheless, there is a contender: composition. This technique is realized by making an object a member of another.



For the above example we say an object of  is composed of, apart from other things, an object of. Put differently, we say an object of  has-an (contains) object of. This is certainly different than inheritance where the relationship is defined to be an is-a relationship.

In C++, similar effect can be achieved through the so-called private inheritance.



Interface (List)
List

What follows is the definition of a nested class, a class defined inside another. Such a class, when defined in a  section, is not visible outside its enclosing class. This scheme is useful when the two classes are tightly coupled, as is the case in our example: A  object is used only in the context of a   object. What makes up our  objects is an implementation detail and should not be a concern to their users.

Although it might be tempting to draw a parallel between nested classes and inner classes of Java, that would be a mistake. As opposed to the special relation between the inner class and its enclosing class, in C++ the enclosing class has no special access privileges with regard to the classes nested within it. For this reason, changing  to   or   is not a good idea.

Another remark to be made is that nested classes of C++ do not keep any record of the enclosing object in the objects of the inner class, which makes them like more the static inner classes of Java.

A function declared in the  section? Yes! Functions, which are used by other functions and are not part of the interface, are declared in the  section. Note that you cannot get away without declaring such functions in the class interface.

Implementation (List)
List.cxx

Now that nodes of the  objects are allocated on the heap, we must make sure they are turned back over to the memory allocator as the   object itself is, implicitly or explicitly, deleted. For this reason, we need to write a destructor to free these nodes. Note a  object is made up of two pointers, which show the head and the tail of the list, and a field holding its size. The nodes pointed, directly or indirectly, by these pointers are not part of the  object. So, they will not be automatically freed together with the list object. For this reason, we do need a destructor.

Notice our decision is not affected by whether the  object itself is created on the heap or not. Where the  object is created has an effect on who should call the destructor: Whoever the responsible party might be, compiler or programmer, in all possible scenarios the destructor is implicitly called before deallocation of the object. If it is created on the heap the programmer is responsible for making the call. Otherwise, the compiler will take care of the drudgery as the scope of the object is closed.

Interface (Stack)
Stack

The  class offers a superset of the functionality expected from a stack. This may at first lead us to think that we may define a new class,, and have it (publicly) derive from the   class. The problem with this approach is that the public interface of the base class will be exposed as part of the public interface of the derived class. Not really what we would want in this case: the  class offers a lot more than what we would expect from the   class. For this reason we should resort to some other method, such as composition.

C++ offers an alternative: private inheritance. Using private inheritance, the derived class can still make use of the functionality offered by the base class but the base class interface is not exposed through the derived class. For this reason,  class privately inherits from the   class.

Now that the derived class can reuse the base class functionality but do not expose it to its users, this type of inheritance is also called implementation inheritance. For a similar reason, public inheritance is also called interface inheritance.

We do not need to write the functions of the orthodox canonical form because compiler-synthesized versions provide the equivalent of what we are required to do. This is basically because the only data field of  is the   sub-object it inherits from.

Thanks to the following statement, we selectively expose a function from the privately inherited base class. It’s as if the  function(s) from the   class were publicly inherited.

Implementation (Stack)
Stack.cxx

Users of our class should not be aware of how we implement the  class. That’s why we need to re-throw the exceptions thrown by the  class so that it makes more sense to the user.

Virtual Inheritance
With the possibility of multiple inheritance rises the issue of the so-called virtual inheritance. Consider the class hierarchy shown in Figure 2. Question that awaits your answer is: How many  sub-objects will there be in a   object? Looking at the figure the correct answer seems to be two. However, our logic tells us a different story: there can be only one  sub-object in a.



Whichever one is the right answer, there might be cases where either one turns out to be a better choice. We must find a way to tell the difference between the options. This is where the notion of virtual inheritance comes into the picture. We define the  and   classes to be virtually derived from.

Thanks to these definitions, there is now only one  sub-object in a. This is achieved by assuring that pointers&mdash;not objects themselves&mdash;are inserted into the derived classes. That is; instead of containing two  sub-objects, a   object now has two pointers both pointing to the same   sub-object.

Note use of virtual inheritance causes the order of constructor call to be changed: Virtual base classes are always constructed prior to non-virtual base classes regardless of where they appear in the inheritance hierarchy.

A typical use of virtual inheritance involves implementation of mix-in classes. Mix-in classes are used for tuning the behavior of a base class and can be combined to obtain even more specialized classes. For example, using the following code one can create windows with different styles: plain window, window with menu, window with border, and window with menu and border. As a matter of fact, we can come up with our own mix-in, say scroll-bar mix-in, and get scroll-bar-supporting versions of these window styles.

Note the number of window styles grows exponentially with the number of mix-ins. But, thanks to virtual inheritance, we do not have to consider each and every combination. We start out with the base class and a few mix-ins. As we need more refined window styles we come up with a new class inheriting from the relevant mix-in classes. If we see certain attributes are missing from the mix-in classes we can write our own mix-in and use it like the others.

Implementing Polymorphism
In this section, we take a look at two widely used methods of implementing polymorphism. It is worth noting that both rely on dynamically dispatching the function call to the relevant [function] entry point. In other words, polymorphism is implemented by means of dynamic dispatch. Another point to make is the tool we use for expressing polymorphism: inheritance.

Mentioning polymorphism, inheritance, and dynamic dispatch in different contexts may make some think of them as unrelated concepts. This, however, is utterly wrong. Truth of the matter is, in an object-oriented context these are complementary concepts and cooperate for enabling the implementation of the is-a relation in a natural way. In order to express an is-a relation between two classes we need help from both inheritance and polymorphism: inheritance is used to define a common message protocol between the base and derived classes, whereas polymorphism is needed for providing the behavioral variability. This is further made possible by dynamically dispatching the messages.

As was mentioned in previous section, inheritance without polymorphism leads to inflexible, object-based solutions. Likewise, polymorphism alone is generally not what you want. So, it is well-advised that you consider using these two together.

Previous remarks should not make you think we are bound to use the duo of inheritance and polymorphism only. Our success in software industry depends on producing reliable, easily-extensible, efficient software. The keyword in reaching this goal is reuse and aforementioned concepts are not without alternatives. Apart from the age-old composition technique, we can use generics for instance. Parameterizing a class or a subprogram will also give us the benefits of reuse. An example to the former is given in the Parameterized Types chapter, while use of the latter is provided below. Thanks to this method, users can sort any array provided that a  object for the component type is also supplied.

Such a method is said to provide parametric polymorphism. It is polymorphic in the sense that same method does the same thing for parameters of different types; to the user of the method, it looks as though there were separate methods for each different type.

A similar effect can be experienced in the case of overloaded subprograms, where calls with different argument lists are dispatched to subprograms with different signatures and users get different behavior as a result of calling the seemingly same subprogram. For this reason, overloading is sometimes referred to as ad-hoc polymorphism.

Having done away with the confusion about inheritance and polymorphism, let's move on to the techniques commonly used in implementing polymorphism. But before we do that, we will give a listing of the sample classes used in our presentation.

vtables
The vtable technique, generally used in compilers of the PC world, utilizes a table containing rows of function entry address and offset pair. Objects of all classes with at least one dynamically dispatched function has a pointer, called the vptr, pointing to the start of this table. The offset column is used for adjusting the value of the  pointer. This adjustment is required when an object of a derived class is used through a pointer of a base class. Take the definition of, whose object memory layout is given below. Given an object of, we can use it through pointers of   and any type that is an ancestor type of  , which in our case are  ,  , and. Put another way, through a variable of type  we can manipulate any object that is an instance of a class deriving from , which in our case are   and. Accordingly, the following is possible.

Having overridden  in the derived class  means invoking this version of   will potentially make use of all properties found in ,  , and  , which implies the receiver object of this function must at least be a   object. As was mentioned before such an object can also be manipulated through. This is exemplified by executing line 7 of the above fragment following the call on line 15. Note also, upon executing the function call on line 14, the function call on line 7 is dispatched with a  object. Since  is of type , both cases are handled via the vptr field of a   object. However, in one case this object is  part of a   object whereas in the other it is a plain   object. This is shown in figures above.

as part of a  object needs our attention. Starting address of the object and the  pointer used for manipulating this object  indicate different locations in memory. This requires that&mdash;in order to enable use of all properties of a  object&mdash;we adjust the pointer value as many bytes as there are before the   sub-object, which is referred to as delta in our figure.

Adjustor Thunks
The second technique we will look at can be seen as a less portable optimization on the first one. As in the vptr technique, one column in the vtable contains a pointer-to-function. But we now utilize thunks instead of the adjustment column. In case an adjustment to this is needed, pointer-to-function in the one and only column of the vtable points to the thunk code generated by the compiler, which modifies this and jumps to the function entry. If no adjustment to this is required, the pointer-to-function contains the address of the entry of the function to be invoked.

Insert Adjustor Thunks figure here

Complications due to Multiple Inheritance
Observe implementation of polymorphism is complicated by requirements of multiple [implementation] inheritance. Did we content ourselves with single inheritance an object would always have a single vptr and we would never need to make any adjustments to this.

Question: Do we face the same complications while implementing multiple interface inheritance? As a starting point consider memory layouts of objects of a class that implements multiple interfaces or realizes an interface that multiply inherits from other interfaces.

Constructor and Destructor Call Order
Object construction involves allocating memory for the object and initializing its contents [and acquiring outside resources, if needed] by means of a call to the relevant constructor. This atomic sequence is triggered on three conditions:


 * 1) A variable with static extent is defined: In the case of a global variable, memory for the object is allocated in the static data region at compile time and constructor call [that is, initialization and resource acquisition] is executed as the first statement of the program. If there is more than one such variable, constructors are executed in the order in which the corresponding definitions occur in the text. Static local variables differ in two aspects: call to the constructor is executed once upon first entry to the function and this call does not modify the order of statement execution in the function.
 * 2) A block variable is defined: Both allocation and initialization take place at run-time each time control flow reaches the point of the variable definition.
 * 3) An object is created using the new operator: Object is allocated and initialized as the control flow reaches the statement where the new operator is applied.

In case there may not be any programmer-defined constructors, the C++ compiler synthesizes a default constructor that makes calls to default constructors of the sub-objects. In an object with primitive type fields&mdash;since primitive types do not support the notion of constructor&mdash;this means no constructor call is ever made, which leaves the newly created object in a random state.

Complementing construction, destruction of the object is accomplished by a call to the destructor function, which is followed by release of the object's memory. The destructor, if there is any, is meant to release resources- both heap memory and outside resources- being used by the soon-to-be recycled object.

Destructor invocations of static variables take place following the last statement of the program while block variables are destroyed upon exit from the block they are defined in. In the presence of multiple variable declarations in the same scope, the order of destructor calls for both kinds of variables is the reverse of the constructor call order.

An Object with Primitive Type Fields
Unlike Java and C#, where primitive type fields are guaranteed to have specific initial values, C++ does not perform any implicit initialization of such fields. In other words, unless provided by the programmer, such fields are left uninitialized.

An Object Composed of Sub-object(s)
In the process of creating a composite object, a call to the constructor [of the composite object] is preceded by the relevant constructor call(s) for the sub-object(s). Unless these calls are explicitly made in the member initialization list, sub-objects are initialized using the default constructor(s).

In the case of multiple sub-objects constructor calls are made in the order in which they occur in the class definition.

Creating an Array of Objects
A facility to define group of variables belonging to the same type, an array variable is initialized by initializing each and every one of its components. Similarly, destroying this array requires destruction of each and every one of its components.

Inheritance
Seeing inheritance as "compiler-managed composition" is key to figuring out constructor and destructor call order. Rest is all the same.

First line of the above fragment can be thought of having been converted by the compiler to the following. Note the identifier name is arbitrary and cannot be in any way referenced in the class implementation.

Member Initialization List
Unless otherwise told all implicit constructor calls are made to the default constructor. This behavior can be changed by appending member initialization list(s) to the function header of the constructor(s).

For pedagogical purposes, line 4 of the above fragment can be seen as the following. However, since it replaces two sequences of "an initialization followed by an assignment" with two initializations, using a member initialization list is a more efficient choice. This is because&mdash;even when you don't have a member initialization list&mdash;constructor(s) of the sub-objects are called before that of the composite object, which means the two lines in the following fragment are actually assignments, not initializations. Before> they get executed each sub-object will have already been initialized using the default constructor of.

Multiple Inheritance
Building on our informal definition of inheritance as compiler-managed composition we can treat objects of multiply inheriting classes as being composed of more than one sub-object. Therefore, for pedagogical purposes, we can accordingly consider line 1 of the following fragment as below.

Restating the Formula: Inheritance is Compiler-Managed Composition
What follows are three pairs of equivalent class definitions meant to provide an insight into what the compiler accomplishes behind the scenes. While perusing through the code keep in mind that code given in the right column reflects only what the compiler does, not how it does it.

Public inheritance enables use of base class interfaces through that of the derived class. No effort is required on the programmer side, all is taken care of by the compiler. If for some reason you want to do it without inheritance, you must explicitly expose the functions of the base class and delegate calls to these functions to the corresponding functions in the base class. With private inheritance and selective exposition of the base class interface, this extra burden is lessened for C++ programmers.

Private derivation means the functionality in the base class is not visible through an object of the derived class. However, it is still possible to utilize this functionality in implementing the functions found in the interface of the derived class.

Sometimes a mixture of the two cases may be needed. That is; part of the functionality in the base class is visible and rest has to to be hidden. This selective exposition can be accomplished by a combination of private inheritance and the  declaration.

Virtual Inheritance
Object of a virtual base class is always constructed prior to the objects of non-virtual classes. It should be kept in mind that "virtualness" is actually the property of the derivation, not that of the base class itself.