12.1 — Pointers and references to the base class of derived objects

In the previous chapter, you learned all about how to use inheritance to derive new classes from existing classes. In this chapter, we are going to focus on one of the most important and powerful aspects of inheritance -- virtual functions.

But before we discuss what virtual functions are, let’s first set the table for why we need them.

In the chapter on construction of derived classes, you learned that when you create a derived class, it is composed of multiple parts: one part for each inherited class, and a part for itself.

For example, here’s a simple case:

When we create a Derived object, it contains a Base part (which is constructed first), and a Derived part (which is constructed second). Remember that inheritance implies an is-a relationship between two classes. Since a Derived is-a Base, it is appropriate that Derived contain a Base part.

Pointers, references, and derived classes

It should be fairly intuitive that we can set Derived pointers and references to Derived objects:

This produces the following output:

derived is a Derived and has value 5
rDerived is a Derived and has value 5
pDerived is a Derived and has value 5

However, since Derived has a Base part, a more interesting question is whether C++ will let us set a Base pointer or reference to a Derived object. It turns out, we can!

This produces the result:

derived is a Derived and has value 5
rBase is a Base and has value 5
pBase is a Base and has value 5

This result may not be quite what you were expecting at first!

It turns out that because rBase and pBase are a Base reference and pointer, they can only see members of Base (or any classes that Base inherited). So even though Derived::getName() shadows (hides) Base::getName() for Derived objects, the Base pointer/reference can not see Derived::getName(). Consequently, they call Base::getName(), which is why rBase and pBase report that they are a Base rather than a Derived.

Note that this also means it is not possible to call Derived::getValueDoubled() using rBase or pBase. They are unable to see anything in Derived.

Here’s another slightly more complex example that we’ll build on in the next lesson:

This produces the result:

cat is named Fred, and it says Meow
dog is named Garbo, and it says Woof
pAnimal is named Fred, and it says ???
pAnimal is named Garbo, and it says ???

We see the same issue here. Because pAnimal is an Animal pointer, it can only see the Animal portion of the class. Consequently, pAnimal->speak() calls Animal::speak() rather than the Dog::Speak() or Cat::speak() function.

Use for pointers and references to base classes

Now you might be saying, “The above examples seem kind of silly. Why would I set a pointer or reference to the base class of a derived object when I can just use the derived object?” It turns out that there are quite a few good reasons.

First, let’s say you wanted to write a function that printed an animal’s name and sound. Without using a pointer to a base class, you’d have to write it using overloaded functions, like this:

Not too difficult, but consider what would happen if we had 30 different animal types instead of 2. You’d have to write 30 almost identical functions! Plus, if you ever added a new type of animal, you’d have to write a new function for that one too. This is a huge waste of time considering the only real difference is the type of the parameter.

However, because Cat and Dog are derived from Animal, Cat and Dog have an Animal part. Therefore, it makes sense that we should be able to do something like this:

This would let us pass in any class derived from Animal, even ones that we created after we wrote the function! Instead of one function per derived class, we get one function that works with all classes derived from Animal!

The problem, is of course, that because rAnimal is an Animal reference, rAnimal.speak() will call Animal::speak() instead of the derived version of speak().

Second, let’s say you had 3 cats and 3 dogs that you wanted to keep in an array for easy access. Because arrays can only hold objects of one type, without a pointer or reference to a base class, you’d have to create a different array for each derived type, like this:

Now, consider what would happen if you had 30 different types of animals. You’d need 30 arrays, one for each type of animal!

However, because both Cat and Dog are derived from Animal, it makes sense that we should be able to do something like this:

While this compiles and executes, unfortunately the fact that each element of array “animals” is a pointer to an Animal means that animals[iii]->speak() will call Animal::speak() instead of the derived class version of speak() that we want.

Although both of these techniques could save us a lot of time and energy, they have the same problem. The pointer or reference to the base class calls the base version of the function rather than the derived version. If only there was some way to make those base pointers call the derived version of a function instead of the base version…

Want to take a guess what virtual functions are for? 🙂

Quiz time

1) Our Animal/Cat/Dog example above doesn’t work like we want because a reference or pointer to an Animal can’t access the derived version of speak() needed to return the right value for the Cat or Dog. One way to work around this issue would be to make the data returned by the speak() function accessible as part of the Animal base class (much like the Animal’s name is accessible via member m_name).

Update the Animal, Cat, and Dog classes in the lesson above by adding a new member to Animal named m_speak. Initialize it appropriately. The following program should work properly:

Show Solution

2) Why is the above solution non-optimal?

Hint: Think about the future state of Cat and Dog where we want to differentiate Cats and Dogs in more ways.
Hint: Think about the ways in which having a member that needs to be set at initialization limits you.

Show Solution

12.2 -- Virtual functions and polymorphism
11.x -- Chapter 11 comprehensive quiz

60 comments to 12.1 — Pointers and references to the base class of derived objects

  • CodeBee

    In question no . 2 , Can u please elaborate the lines if possible with an example:
    "Also, this solution only works if the base class member can be determined at initialization time. For example, if speak() returned a randomized result for each Animal (e.g. calling Dog::speak() could return “woof”, “arf”, or “yip”), this kind of solution starts to get awkward and fall apart."

    • Alex

      No, but you can easily imagine how it might work. In your base class, you’d need to store an array of possible sounds that your animal could make. Your derived constructor would initialize these. The speak() function in the base class would select one randomly. Each Animal object would incur the memory cost of all of those strings.

  • Aakash

    Hello Alex,
    In the answer of quiz no 1), the line 54 must be in comments. But the solution miss the comment declaration

  • ManiKanth

    Hi Alex!
    Why is the initialization part of cat’s constructor like Animal(name) instead of m_name(name)??

    • Alex

      Because Cat is derived from Animal, Cat is invoking the Animal constructor to initialize the Animal portion of Cat. This reduces redundant code (Cat doesn’t have to do the same thing Animal can already do).

  • ManiKanth

    Hi Alex! I have not gone through all the comments so I don’t know if its been covered.
    Why can’t I just avoid the usage of member function speak() (which are obviously different in the classes cat, dog and animal), but instead put a private const data member "sound_name" in all the classes and use a single public access function speak() in the base animal class which can be inherited by the classes cat and dog?I mean, just like you used m_name and getName(), why can’t you use sound_name and speak()? I think the use of virtual functions can be avoided if this is the case because of no overrides. Is there something I’m missing on it?

    • Alex

      Quiz question 1 in this chapter poses this very solution, and quiz question 2 explains some of the reasons why it’s not optimal to do that.

      • ManiKanth

        Sorry for overlooking the quiz questions. Now I have a doubt in quiz question 2.

        "The current solution is non-optimal because we need to add a member for each way we wanted to differentiate Cat and Dog. Over time, our Animal class could become quite large memory-wise, and complicated!"
        This I get.
        But what I don’t get is - Is your claim that
        "Also, this solution only works if the base class member can be determined at initialization time. For example, if speak() returned a randomized result for each Animal (e.g. calling Dog::speak() could return “woof”, “arf”, or “yip”), this kind of solution starts to get awkward and fall apart."
        only true for the quiz example? The "pointer-to-base-class" version of it has also the same issue right? Or if speak() has to return a randomized result for each animal, then is there a solution to come over this by using pointer to base class?

        • Alex

          I’m not sure I quite understand where your confusion is. But yes, there is a solution to overcome this issue (virtual functions), which is what the rest of this chapter is about. This is just setting up all of the context for understanding why that solution exists in the first place, and what it solves for.

  • Himanshu

    These tutorials are great. i want to learn data structures, linked lists and queues.
    which site do you prefer? Any suggestions?

  • navid

    Why did you use const char * speak?Why not use string ?maby const string !

  • Bing

    Hello Alex

    I have a question for you about following lines

        Base(int nValue)
            : m_nValue(nValue)

        const char* GetName() { return "Base"; }
        int GetValue() { return m_nValue; }

    Can I do it without asterisk after char? and What is benefit of using pointer here.

    • Alex

      Sure, you could return a value of type std::string instead (or any other type that C-style string “Base” can be converted into). But that means every time you call GetName(), the program will construct a std::string, which is a lot slower than just returning the C-style string “Base”.

      The benefit of using a pointer to a C-style string here is that it’s fast, since no conversions need to take place.

  • Anddo

    It’s kinda not on topic question but it’s surely relative. a bit confused and need direct clarification

    code like this using const char (or char) pointer to handle c-style string

    or this

    This code has a pointer initialized with value !!!. I never seen it before but I read that string literal is special case which actually "lvalue expression, referring to an array object (not a pointer)". That make sense. However that raises my next question, if the string literal is lvalue then is it temporary ? what is its duration ?

    Also it is created and a pointer tmp pointed to its address inside geteex function so tmp pointer has local scope, right ? so when tmp is returned (pass by address) and the address assigned to catchchar in the main, in all that, the string literal still there and actually outputted on the console.

    Notice that I didn’t use const char but I used char to know if it’s stored in anonymous variable (since const extends the duration like in const reference) but that seem didn’t make difference.

    Sorry for the long question. Please clarify this to me since it holding me confused. And as always, thank you for everything 🙂

    • Alex

      I talk about C-style string literals in section 6.8b. In short, they’re stored in a special area of memory and persisted throughout the life of the program, so we can return pointers to them without having to worry about scoping issues. In your code, pointer tmp has local scope and gets destroyed at the end of the function, but the C-style string literal “Hello” does not get destroyed, so the caller (main) can still access it.

  • Turya


    To access speak() in the derived class via base pointer or reference, can we forward declare derived class and make it ‘friend’ with base class?

    • Alex

      I’m not sure what you mean by “can we forward declare derived class and make it ‘friend’ with base class?”

      • Turya

        I meant something like -

        • Alex

          You can… but I don’t think that helps your situation any.

          A pointer or reference to a base class object only knows about base class members. So unless you were to cast it to a Derived first, you wouldn’t be able to access speak() in the Derived class.

          Keep reading though. Virtual functions solve this issue in a way more elegant way.

  • Reaversword

    Ok. I get things a little bit far from lesson, and I have some doubts and observations.

    Since we are working with classes, I’m trying to get the dog & cat & animal example working in the heap using a vector, in place of an array inside the stack.

    But, when I go with this:

    vector<Animal> Varr {Cat("Fred"), Dog("Garbo")};

    I realized several points:

    1) Animal class vector would allocate in every of its members enough memory for an "Animal class" object fits…  but Cat or Dog shouldn’t fit! It would fit only its "animal" part!.
    …so, would be impossible to use virtual functions to reach Speak()’s info from derivated classes?. Or C++ fixes this allocating more memory per vector member to fit base+derived size?.

    2) I had no idea about how to initialize Cat or Dog inside a vector declaration… But I think I’ve found the way to initialize a "Cat" or "Dog" class inside an "Animal" vector (that way I’ve written). vector<Class> vectorIdenfitier {Class{parameters, …}, Class{parameters, …}}, without member’s vector identifier, since Vector[position] is the implicit identifier. Is that right or that way haves some downsides?

    3) Obviously, create dogs & cats out of the vector would be nonsense, because if you create Dogs and Cats out of the vector, they wouldn’t be in the heap (failing the exercise of do it in the heap), and wouldn’t be a way to bring that to the vector by reference (since ones are in the stack and the other one, in the heap). It should be copied, and this implies twice memory and performance loss.

    I leave here some code:

    • Reaversword

      Of course, when I turn functions "speak()" to virtual, I still receive the Base message(because Animal vector doesn’t store Dog’s or Cat’s derived part, I guess).

      I feel myself close but I can’t see how to continue right now.

      I’m looking for the way to get it working, but still no lucky. This is my last attempt:

      But if the most-derived part is not stored in vector-array, it won’t be possible to find "Meows" and "Woofs".

      I’ve tested in more simple ways, but the same result. This was a little bit more messy.

      How can we do this with dynamic memory?.

    • Alex

      If you allocate a vector, you will get a vector of Animals. If you assign a Dog or Cat to an element of this vector, you’ll only get the Animal part.

      What you should do is allocate a vector of Animal pointers, and then dynamically allocate your Dogs and Cats:

      Also, don’t forget to delete the pointer before your array goes out of scope, otherwise you’ll have a memory leak.

      • Reaversword

        Ok, It works perfectly. Thanks Alex. It not even needs to use last covariant method from next lesson I tried using yesterday.

        But this makes me question something:
        anVrray is a vector array of Animal type pointers, so every member of the vector array is just a memory address. So when we declare "new" in the initialization, system creates a pointer of type Cat/Dog and stores there, but this member will be interpreted by vector array as a pointer of type Animal because the vector array type.
        So, "real" Cat/Dog object is allocated in other random place of the memory, not necessary consecutive addresses, isn’t it? (Obviously, never are in the vector, there is only a pointer to them).

        I hope to have catched it up.

        Finally, some code, to complete.

        So if we want to do it in the heap, we will need a little bit more of memory for the pointers to Cats&Dogs (the vector pointers array), and performance loss for the extra steps this pointers implies, I guess.

        Very smart Alex, in the method to get an "Animal" vector pointing to a "real" Cat/Dog. Priceless lesson.

        • Alex

          The Cats and Dogs allocated dynamically may not be consecutive in memory. Only the array elements are guaranteed to be consecutive (since they’re all allocated from one block of memory).

          Yes, the heap version uses a little bit more memory because you need the extra pointer. That’s the price for all this polymorphism I guess.

  • Mr D

    Sorry, one more question:

    In the first example with dog & cat, you write:

    Why don’t you just write:

    Is there a reason why you’re mixing these two different ways of outputing a string together?

    • Alex

      Mainly because m_strName is of type std::string, whereas “???” is of type const char*.

      We could have Speak() return a std::string, but that would entail contructing a std::string object every time the function was called. Better to just return the const char* and let the user put it in a std::string if they want to.

  • Mr D

    Hi Alex,

    I’m a little puzzled by the following line from the the first example:

    Actually, if we forget about the ref part, we could just say:

    So what are we actually saying here (with my simplified version)?
    If rBase = cDerived, then what is equaling what?

    I built the following simple example to help myself understand it, but i’m still not quite getting the actual chain of events that occurs when i execute

    in my example below!

    • Alex

      This makes a copy of the Base portion of MyDerived into variable MyBase (this is called slicing).

      If MyBase were a reference instead, it wouldn’t make a copy, but would refer to the Base portion of MyDerived.

  • tata

    Hi, I went to sections 1.3, 6.2, 8.14, 9.11, and B.4 for review, but have yet to confidently work out the following:

    Could someone help confirm that, in the line above:

    1) The code is performing an initialization (i.e., defining acCats and giving acCats initial values at the same time)

    2) Cat("Fred") is an anonymous object created using the class constructor Cat(std::string strName), it has expression scope and will go out of scope/be destroyed before Cat("Tyson") is instantiated

    3) B/c we are performing an initialization, the compiler then calls the default copy constructor of Cat to create a Cat object at acCats[0] (we are not using the assignment operator here)

    4) acCats[0] is not the same object as Cat("Fred"), in other words, we’ve not somehow extended the life of Cat("Fred") (unlike how a const reference to a literal value would extend the life of that literal value - section 7.4a)

    5) would the following extend the life of the anonymous object Cat(“Fred”)

    Thanks in advance and sorry for such long winded simple questions; I’ll search for suitable forums so I won’t clutter the comment section in the future

    • tata

      For question number 5, I meant to write

    • Steiner

      From my understanding of the lessons:

      1) If you recall on lesson 6.1, An array is simply allows us to access many variables of the same type through an identifier. So you can think of the array acCats as being a set of these variables:

      which could be better off being written as

      or as

      2) So as you see from 1), Cat("Fred") is not really an anonymous object since this gets assigned to an element inside an array. It doesn’t have an expression scope since the array element will contain this data.

      3) We are performing an initialization but we are simply calling the constructor (not copy constructor) since we simply constructing the object and assigning it to a variable or in this case, an array element. There is no existing object before the copying can occur for the copy constructor to be used. The default assignment operator is used in this case, in which memberwise/shallow copying occurs.

      4. acCats[0] contains the object Cat("Fred") due to using uniform initialization. The life of Cat("Fred") depends on whether the array acCat goes out of scope of the destructor for the Cat("Fred") is invoked.

      5. The code

      would simply change the data type of the array to contain const references. So it won’t be possible to assign values to the elements in the array acCats after initialization. Again as answered in 2), these are not anonymous objects as they get assigned to an array element so these objects will only get destroyed once it reaches out of scope when the destructor of this object gets invoked.

  • JaSoN

    Can you explain to me why we should do: const char* instead of just const char ?
    Thank you so much

    • Devashish

      If you are talking about the functions that return const char*, the answer is, because values returned by those functions are not individual characters (such as ‘a’, ‘b’ or ‘1’) but C-style string literals (such as “Base”, “Derived” or “???”). Is that clear?

      • Alex

        Just to clarify, const char* is a pointer to a character, which can also be used to point to a C-style string (such as “Meow”). Since Speak() returns more than one character, we need to return a string of some kind.

        We could also have it return a std::string.

        • Mekacher Anis

          I know that’s a C-style string , but I also think that an anonymous variable would be created and than it would be destroyed , so the caller should copy the value returned to another C-style string ? Am I right ?

  • anonymous

    The best tutorial I have ever read and not just for programming. LearnCPP is a hell of a good work!

  • pranesh

    In the animal example, the snippet down here says that since panimal is an animal pointer and so it can only see inside animal then how come it prints the name of cat as fred but prints speaks as ‘???’.please expain me.

    int main()
    Cat cCat(“Fred”);
    cout << "cCat is named " << cCat.GetName() << ", and it says " << cCat.Speak() << endl;

    Dog cDog("Garbo");
    cout << "cDog is named " << cDog.GetName() << ", and it says " << cDog.Speak() << endl;

    Animal *pAnimal = &cCat;
    cout << "pAnimal is named " <GetName() << ", and it says " <Speak() << endl;

    Animal *pAnimal = &cDog;
    cout << "pAnimal is named " <GetName() << ", and it says " <Speak() << endl;

    return 0;
    This produces the result:

    cCat is named Fred, and it says Meow
    cDog is named Garbo, and it says Woof
    pAnimal is named Fred, and it says ???
    pAnimal is named Garbo, and it says ???

    • Goutham

      same doubt can one one explain

    • Goutham

      got it.

      when you create a derived class, it is composed of multiple parts: one part for each inherited class, and a part for itself.

      So Base pointer points to one part of inherited class.

      indeed, it prints names of dogs…

      • Christof

        nope. the base class can’t see anything from derived classes (except when working with virtual members, as explained in the next chapter). the reason why in this case a pointer to Animal will print the Cat’s or the Dog’s name, is because constructing a Cat or a Dog will initialize the string object m_strName - which is part of the base class Animal!

        • Alex

          Yes, this. Because the Animal’s name is stored in the Animal class, it’s available to all Animals (including Animal references or pointers). However, because speak() needs to know whether the object is a Cat or a Dog to resolve correctly, we can’t get to the right version of that function with just an Animal reference or pointer.

  • HJ

    Hmm…Alex, I am speechless. I am relearning C++ again. Most of the stuffs here, I just took them upstairs over a period of coding but never knew why things were so. I thank you so much for clarity and simplicity. You have done more than a professor would do. Thanks

  • hfs

    I am enjoying every bit of this tutorial. it contains everything in which i always used to stuck while programming in C++.
    Thanks a lot

  • pshetty

    great tutorial alex…wish you many more newcastles 🙂

  • shivamgarg100

    okay, the prob got clear!!

  • shivamgarg100

    what does this mean can anyone eloborate
    Derived &rDerived = cDerived;
    cout << "rDerived is a " << rDerived.GetName() << " and has value " << rDerived.GetValue() << endl;

  • Biraj

    So far the Best Website to understand C++ …Great work done by Alex !!!! Thanks a lot it helps a lot in understanding C++ in depth …

  • Deepak

    I have no word for this site , thanks alot for such a nice tutorial.

  • This is one of the best article I found on the internet for the need of base class pointer or references for objects. It is easy to understand the virtual function mechanism and the syntax but really painful to udnerstand the need when we can achieve interclass polymorphism using the derived class object.

  • I think this is the best tutorial whatever I have read ….

  • Wei

    This is a great website and the contents here are pretty good for studying C++.
    As a graduate student in Computer Science, I am very thankful to Alex. This is great!!

  • Kinten

    Everyting ok, butttt!, you can’t call a cat “tyson”!! that’s a dog name :[

  • Nassos Katsadim

    Thank you for this tutorial. This is a fantastic prephase for understanding how late binding and polymorphism works

Leave a Comment

Put C++ code inside [code][/code] tags to use the syntax highlighter