The static type is the type of an object variable as declared at compile time and the dynamic type is the type of an object variable determined at run-time. In most cases the static and dynamic types of an object variable are the same, however, they can differ when we are using pointers to objects. An example of this capability is demonstrated below:
Account a = Account(35.00, 34234324);
Account *ptrA = &a;
CurrentAccount b = CurrentAccount(50.0, 12345, 200.0);
CurrentAccount *ptrB = &b;
cout << "Displaying ptrA:" << endl;
cout << "Displaying ptrB:" << endl;
//ptrB = ptrA; // not allowed
ptrA = ptrB;
cout << "Displaying ptrA again:" << endl;
A full source code example for this is in
The output from this code segment is shown as in Figure 3.4, “The output from the code segment above.”
Figure 3.4. The output from the code segment above.
To try to explain this further, see the following figures:
Figure 3.5, “The pointers
ptrB have the same static and dynamic types.” shows the code segment after steps 1 and 2 have taken place. This means that the two objects are created in memory and the two pointers point at their respective objects, so an
Account pointer points at the
Accountobject and the
CurrentAccount pointer points at the
Figure 3.6, “Allowed: The pointer
ptrA has static type
Account and dynamic type
CurrentAccount.” shows the state of the application after the 6th step takes place. The
Account pointer is assigned to the
CurrentAccount pointer and this is allowed. To understand this, you can think of the
Accountpointer as understanding the
makeWithdrawal() methods. So, we can call any of these methods on the
ptrA and the object that we are calling these methods on understands all these methods (and more!). This is guaranteed in this case! why? The
CurrentAccountclass is a child of the
Account class and so has inherited all the methods from
Account. Therefore, calling the methods of the pointer to the
ptrA works perfectly.
Figure 3.7, “Not Allowed: Pointer
ptrB has static type
CurrentAccount and dynamic type
Account.” shows the assignment of
ptrB = ptrA; which is not allowed. If this case was allowed then the
ptrB would be pointing to an
Account object. But the
CurrentAccountpointer understands the methods
setOverdraftLimit(). So what would happen if the
setOverdraftLimit() was called on the
a? It does not have a
setOverdraftLimit() method and so it would fail.
The rule is: An assignment L=R, is legal only if the static type of R is the same or a derived type of the static type of L.
One very important point to note in the code segment above is that when the pointer
ptrA of static type
Account points at the
CurrentAccount object and its
display() method is called, it is the
display() method of the object
CurrentAccount is called, not the
display() method. You can see this in the screen grab of the output, as shown in Figure 3.4, “The output from the code segment above.”, where the overdraft limit is displayed the second time
ptrA->display() is called. So the method is called on the dynamic type, not the static type.
The ability of a variable to change its dynamic type during execution is a useful property of C++, allowing programs to be easier to extend and easier to debug. However, it does have some consequences: Take the last example - The base class
Account and the derived class
CurrentAccount have different implementations of the
display() method. The dynamic type of the object determines which
display() method is called. This is called Dynamic Binding. With the facility of dynamic binding,
CurrentAccount::display() is called. If there was no such facility,
Account::display() would always be called.
The notation that I just used for identifying the two
display() methods allowed a clear indication of which
display() method was being discussed, either the
display() method associated with the
Account class or the
display() method associated with the
CurrentAccount class. The "::" is an actual operator, called the scope resolution operator, and it has very important uses.
For example: The
display() method that was developed for the
CurrentAccount class displayed the balance, the account number and the overdraft limit. However, the code used to display the balance and the account number is the same as that in the parent class
Account::display() method. So code has been replicated - This is unacceptable under the OOP paradigm as later alterations to this segment of code must also be replicated.
The scope resolution operator allow this issue to be resolved, for example, the
display() method can be modified to:
cout << " And overdraft limit: " << overdraftLimit << endl;
Now, the code from the
display() method is not being replicated using cut-and-paste, rather it is being called directly. Why is this so important?
Well, this demonstrates that we are able to call the method that we are over-riding (the
display() method) from within the new method (
Code does not have to be re-written. Always Good!
Without this feature - If a later modification needs to be made, for example adding an owner name state, or sort code to the
Account class, then all derived class
display() methods would have to be updated.
If a "bug" is found, it need only be fixed once.
It becomes much easier to manage the code.
In this case, only 2 lines of code have been replicated, but it could have been 100's of lines of code, with 10 different types of derived classes.