Concepts / Accessing Derived Class Member Functions

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.

  • Programming

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.

Key point

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.

EASY

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.

Shape* pointerdeclared type ShapeShape interfacedraw()Smiley partwink()can callcannot call directly
Why can a Shape pointer call draw() but not wink(), even when it points to a Smiley object that has wink()?

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.

shapeShape pointer variableSmiley objectone complete objectShape partexposed through Shape*Smiley partnot exposed through Shape*points tocontainscontains
What does the base pointer point to, and why does the derived part exist even though the pointer type does not expose it?

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.

cpp

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.

read_shape()returnsShape pointerdynamic_cast<Smiley*>asks the questionIs it a Smiley?Smiley pointer validyes branchs->wink()Null pointerno branchSkip Smiley codeshapetestyesnoif checkednever reached
What happens step by step when dynamic_cast<Smiley*>(shape) runs, and which branch allows wink() to be called?

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.

Worked example

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.

  1. 1
    Get the pointer

    shape = read_shape() gives main a Shape pointer. main only knows it as a Shape.

  2. 2
    Ask the question

    dynamic_cast<Smiley*>(shape) asks whether the object is a Smiley and stores the answer in s.

  3. 3
    Check the answer

    The if statement tests s before the program uses it. A null value means the answer was no.

  4. 4
    Call 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.

Result

Because the object is a Smiley, the cast succeeds, the check passes, and wink() runs through s.

cpp
Output
drawing a smiley
the smiley winks
Notes on the code
  • read_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_cast asks 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
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.

MEDIUM

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_cast line.
  • 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 like
    shape->wink(); when shape is a Shape pointer
    Why it fails

    The Shape pointer type exposes only the Shape interface, so wink() is not visible through it.

    Do this instead

    Get 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 like
    Smiley* s = dynamic_cast<Smiley*>(shape); s->wink();
    Why it fails

    The 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 instead

    Store 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 fails

    The 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 instead

    Treat dynamic_cast as a yes or no question and write code for both answers.

Checking Before Calling

MEDIUM

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.

Related concepts

More concepts starting with A