Let us revert back to a more primitive language. We’ll focus only on the organization of the subroutines. The code in listing 1 has the following equivalent form:
Listing 2: | Cequivalentcodeforlisting1 |
First, we have to understand how the data members are represented. Since class B inherits from class A, each class B object has all the data members of class A. This is done by having the data member superA in struct B. We can handle multiple inheritance because we can simply add data members struct C superC if class B inherits from class C.
The magic of C++ (relative to C) is that it recognizes myA is a class A object, and it automatically selects void A::m1(void) for myA.m1(). In the case of the C equivalent code, the programmer is responsible to choose the correct ?_m1 method for myA. Also note that in C, we need to pass &myA to parameter this explicitly, while it is automatic (implicit) in C++.
Line 24 of listing 1 is more interesting. It first static up cast the address of myB to a pointer to a class A object, then proceed to invoke the corresponding m1 method. The C++ compiler has no problem performing the case, as class A is a super class of class B. After the static up cast, the compiler knows from the context that it should use void A::m1(void) as the m1 method. The magical part is that the this pointer is “adjusted” to only point to a part of the myB object.
The C version of this code is more clumsy, as indicated by line 26 in listing 2. The representation of a subclass in C uses a member to represent everything that is inherited from the superclass. As a result, when we have an up cast, we need to specify the address of only that particular member as this when we call a method of the super class.
Note that the C version of up casting does not actually have any C-style cast. It is not necessary, as myB.superA does have the proper type to match the parameter of A_m1.