Accessing Derived Class Member Functions
A base class pointer can refer to a derived class object, but the pointer type decides which member functions the compiler will let you call through it.
Why One Pointer Type Hides Things
Imagine a program that works with many kinds of shapes. It has a function that reads a shape from somewhere and hands it back to the caller. The caller does not need to know exactly which kind of shape it received. It only needs to treat the value as a Shape. This is a useful design, because the rest of the program can handle every returned object through one common interface.
That convenience comes with a cost. Once a value has been returned as a Shape pointer, the compiler only sees the Shape interface. Any member function that exists only in a derived class is no longer visible through that pointer, even when the object really is of that derived type. This article follows one such pointer step by step and shows how to safely reach the derived class function when it is needed.
A base class pointer can refer to a derived class object, but the pointer type decides which member functions the compiler will let you call through it.
Tracing a Shape Pointer Through read_shape
Suppose a program has a function called read_shape() that returns a Shape pointer. The function does not promise which kind of Shape it has in hand. It might be a plain shape, a circle, or a Smiley. Because the return type is Shape, the calling code sees every result as a Shape and nothing more specific.
Now follow one returned value. The moment read_shape() returns, the variable in main holds a Shape pointer. If that object is really a Smiley, the object is a Smiley, but the variable only offers the Shape interface. Every step after this point depends on that one fact: the pointer's declared type is Shape, while the object's actual type may be something more specific.
Before reading on, write down the declared type of the variable that holds the result of read_shape(), and separately write down the actual type of the object it may point to. Are they the same?
Hints
- Look at the return type of
read_shape()first. - The object may be a Smiley even though the pointer is declared as a Shape pointer.
The Hidden Smiley Interface
Consider the Smiley class, which provides a member function named wink(). A Smiley is a kind of Shape, so a Smiley object can be stored and passed around as a Shape pointer. But when the code holds only a Shape pointer, it cannot simply call wink() on it, because wink() belongs to the Smiley part of the interface, not the Shape part.
The diagram shows the key idea. The pointer type exposes the Shape interface, and wink() sits outside that interface. The object itself has both parts, but the pointer only gives access to the part its declared type names.
The derived part is still present in the object. The pointer type simply decides what the compiler allows you to reach through that pointer. So the problem is not that the Smiley part is missing. The problem is that the variable's declared type does not give you a path to it.
This raises the central question: what can we do when we need a function that only one particular derived class provides? The source answers it by asking a direct question about the object at runtime. Is this Shape a kind of Smiley?
Asking the dynamic_cast Question
The dynamic_cast operator asks at runtime whether a Shape is a kind of Smiley. It is written as a cast to a Smiley pointer. Read the line as the question itself. The variable shape is the Shape pointer. The dynamic_cast asks whether that object is a Smiley. The result is then stored in a Smiley pointer, which gives the program a way to reach Smiley-specific members if the answer is yes.
The cast is best thought of as a question with two possible answers, not as a guaranteed conversion. The answer is not always yes. A Shape pointer might refer to a plain shape or a circle, and in those cases the question has a no answer. Testing the result before using it is the safe pattern.
The diagram shows that the program makes a decision at the point where the type check happens. Only the yes branch leads to wink(). The no branch leads away from Smiley-specific code entirely.
Calling wink() only after the cast is checked
read_shape() returns a Shape pointer that actually refers to a Smiley. Use dynamic_cast to ask whether the object is a Smiley, and call wink() only if the answer is yes.
- 1Get the pointer
shape =
read_shape()gives main a Shape pointer. main only knows it as a Shape. - 2Ask the question
dynamic_cast<Smiley*>(shape) asks whether the object is a Smiley and stores the answer in s. - 3Check the answer
The if statement tests s before the program uses it. A null value means the answer was no.
- 4Call through the valid pointer
When the answer is yes, s is valid and s->
wink()can be called. The call is never written directly on the Shape pointer.
Because the object is a Smiley, the cast succeeds, the check passes, and wink() runs through s.
drawing a smiley
the smiley winksread_shape()returns a Shape pointer, but the object it points to is a Smiley.- The commented line shows that
wink()cannot be called directly through a Shape pointer. dynamic_castasks the runtime question and stores the answer in a Smiley pointer.- The if statement checks the result before
wink()is called. - The Shape class declares a virtual function, which the cast relies on to check the real object type.
What do you think happens?
What happens if read_shape() returns a plain Shape object instead of a Smiley, and the same dynamic_cast and if check run?
Reveal answer
The dynamic_cast returns a null pointer, the if check fails, and wink() is never called. Only draw() output from the Shape would appear.
The cast asks whether the object is a Smiley. It is not, so the answer is no, and the null result tells the check to skip the Smiley-only call.
Rewrite the main function so that it calls wink() only if the cast succeeds, and add an else branch that prints a message when the object is not a Smiley. Explain which line asks the question and which line decides whether wink() runs.
Hints
- The question is asked on the
dynamic_castline. - The decision is made by testing the stored pointer in the if statement.
Mistakes When Calling wink() Unchecked
Common mistake Calling a derived class function directly through the base pointer
What it looks likeshape->wink(); when shape is a Shape pointerWhy it failsThe Shape pointer type exposes only the Shape interface, so
wink()is not visible through it.Do this insteadGet a Smiley pointer with dynamic_cast<Smiley*>(shape) and call wink() through that pointer.
Common mistake Using the cast result without testing it
What it looks likeSmiley* s = dynamic_cast<Smiley*>(shape); s->wink();Why it failsThe answer to the question is not always yes. If the object is not a Smiley, the result is a null pointer, and using it is unsafe.
Do this insteadStore the result and test it immediately, for example with if (s != nullptr), before calling any Smiley-specific function.
Common mistake Assuming the cast is a guaranteed conversion
Why it failsThe cast asks a runtime question with two possible answers. A Shape that is a plain shape or a circle will not pass the Smiley check.
Do this insteadTreat dynamic_cast as a yes or no question and write code for both answers.
Checking Before Calling
A function returns a Shape pointer. Sometimes it points to a Smiley and sometimes to a plain Shape. Write the code that calls wink() when the object is a Smiley and prints "not a smiley" otherwise. Then explain why the if test must come before the wink() call.
Hints
- Use
dynamic_cast<Smiley*> and store the result in a Smiley pointer. - A null result means the object is not a Smiley.
Key Takeaways
- A function that returns a Shape pointer lets a program treat many kinds of shapes alike, but the pointer type hides members that only a derived class provides, such as
wink(). - The derived part still exists inside the object; the pointer's declared type only controls what can be reached through it.
dynamic_cast<Smiley*>(shape) asks at runtime whether the Shape is a Smiley, and its answer is not always yes.- Call derived class functions such as
wink()only through the cast result, and only after the check shows that the cast succeeded. - Calling derived functions without checking the cast leads to errors, because the base pointer cannot reach them and a failed cast gives a null result.