Search

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 of it.

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:

cDerived 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:

cDerived 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() is an override of Base::GetName(), 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:

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 ???

We see the same issue here. Because pAnimal is an Animal pointer, it can only see the Animal 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 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 haven’t thought of yet! Instead of one function per animal, we get one function that works with all classes derived from Animal!

The problem, is of course, that because cAnimal is an Animal reference, cAnimal.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 do it 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 Animal, it makes sense that we should be able to do something like this:

While this compiles and executes, unfortunately the fact that apcAnimals is a pointer to an Animal means that apcAnimals[iii]->Speak() will call Animal::Speak() instead of the proper derived class version of Speak().

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? 🙂

12.2 -- Virtual functions
Index
11.8 -- Virtual base classes

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

  • Nassos Katsadim

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

  • Ben

    In the big example above “Use for pointers and references to base classes” (The one with the Dog and the Cat), you got this snippet of code:


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

    Animal* pAnimal = &cDog;


    So pAnimal is declared twice. The second declaration should be changed to:
    pAnimal = &cDog;

    Ben

  • Kinten

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

  • DaJones

    “So even though Derived::GetName() has overridden Base::GetName(), the Base pointer/reference can not see Derived::GetName().”

    In the example you’re talking about (the topmost code) Derived::GetName() has **hidden** Base::GetName() and not **overriden** it.

  • Great stuff!! Took me a while to get it, but I’m there I now.

    I think you need to edit that last example a bit maybe?

    you missed out the
    #include <iostream>

    and the
    using namespace::std;
    after the int main()

    Petty things I know. 🙂

  • 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!!

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

  • 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.

  • [code]
    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.

    [code]

  • Deepak

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

  • 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 …

  • saini

  • shivamgarg100

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

  • shivamgarg100

    okay, the prob got clear!!

  • pshetty

    great tutorial alex…wish you many more newcastles 🙂

  • 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

  • krish

    #include
    #include
    #include

    class Animal
    {
    protected:
    std::string m_strName;

    Animal(std::string strName)
    : m_strName(strName)
    {
    }

    public:
    std::string GetName() { return m_strName; }
    const char* Speak() { return “???”; }
    };

    class Cat: public Animal
    {
    public:
    Cat(std::string strName)
    : Animal(strName)
    {
    }

    const char* Speak() { return “Meow”; }
    };

    int main()
    {
    Cat cCat(“Fred”);

    Report(CCat);
    }
    void Report(Cat &cCat)
    {
    using namespace std;
    cout << cCat.GetName() << " says " << cCat.Speak() << endl;
    }
    how should i call report() and wt parameters need pass at main's report()……….plz…………

  • prakash

    Alex,

    some correction needed on this page.
    Replace:

    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;

    with:

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

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

  • 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

  • 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!

  • or copy dis below:…

    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;

    Bird cbird("Dove");
    cout << "cBird is named " << cbird.GetName() << ", and it says " << cbird.Speak() << endl;

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

    Animal *pAnimal2 = &cDog;
    cout << "pAnimal is named " <GetName() << ", and it says " <Speak() << endl;
    cin.get();
    return 0;
    }

    thanks alot Mr.alex

  • thang

    i use VS2010, copied and pasted:
    #include <string>
    class Animal
    {
    protected:
        std::string m_strName;

        // We’re making this constructor protected because
        // we don’t want people creating Animal objects directly,
        // but we still want derived classes to be able to use it.
        Animal(std::string strName)
            : m_strName(strName)
        {
        }

    public:
        std::string GetName() { return m_strName; }
        const char* Speak() { return "???"; }
    };

    class Cat: public Animal
    {
    public:
        Cat(std::string strName)
            : Animal(strName)
        {
        }

        const char* Speak() { return "Meow"; }
    };

    class Dog: public Animal
    {
    public:
        Dog(std::string strName)
            : Animal(strName)
        {
        }

        const char* Speak() { return "Woof"; }
    };

    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 " << pAnimal->GetName() << ", and it says " << pAnimal->Speak() << endl;

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

        return 0;
    }

    But when i tried build, its showed errors like:
    Error    1    error C2679: binary ‘<<‘ : no operator found which takes a right-hand operand of type ‘std::string’ (or there is no acceptable conversion)    c:\users\haminh\documents\visual studio 2010\projects\1\1\1.cpp    48    1    1

    How can i fix this issue, thanks!

  • anonymous

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

  • Simon

    Hey Guy,
    Thanks very much for this great tutorial.
    I think there’s a mistake in the last code of section "Pointers, references, and derived classes", on line 53/54:

    because you redefine pAnimal, so i think it should either be:

    or you need to create another pointer for instance:

    Cheers

  • Alex, as people are noticing:

    you declared pAnimal twice, so it’ll cause a compile error.

  • 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 ?

  • 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.

  • 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.

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

  • 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.

  • 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.

Leave a Comment

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

  

  

  

9 + 13 =