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:
int main()
{
Account a = Account(35.00, 34234324);
Account *ptrA = &a;
CurrentAccount b = CurrentAccount(50.0, 12345, 200.0);
CurrentAccount *ptrB = &b;
cout << "Displaying ptrA:" << endl;
ptrA->display();
cout << "Displaying ptrB:" << endl;
ptrB->display();
//ptrB = ptrA; // not allowed
ptrA = ptrB;
cout << "Displaying ptrA again:" << endl;
ptrA->display();
}
A full source code example for this is in
CurrentAccount2.cpp
The output from this code segment is shown as in 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 
ptrA and 
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

Account object and the 
CurrentAccount pointer points
at the 
CurrentAccount object.
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 
Account pointer as understanding the 
display(),

makeLodgement() and 
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

CurrentAccount class 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 
Account 
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 
CurrentAccount
pointer 
ptrB would be pointing to an 
Account object. But
the 
CurrentAccount pointer understands the methods 
display(),

makeLodgement(), 
makeWithdrawal() and

setOverdraftLimit(). So what would happen if the 
setOverdraftLimit()
was called on the 
Account object 
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 
Account 
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

CurrentAccount class's 
display() method can
be modified to:
void CurrentAccount::display()
{
Account::display();
cout << " And overdraft limit: " << overdraftLimit << endl;
}
Now, the code from the 
Account's 
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

Account::
display() method) from within the new
method (
CurrentAccount::
display()).
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.
© 2006
Dr. Derek Molloy
(DCU).