In the previous lesson on pointers and references to the base class of derived objects, we took a look at a number of examples where using pointers or references to a base class had the potential to simplify code. However, in every case, we ran up against the problem that the base pointer or reference was only able to call the base version of a function, not a derived version.
Here’s a simple example of this behavior:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include <iostream> #include <string_view> class Base { public: std::string_view getName() const { return "Base"; } }; class Derived: public Base { public: std::string_view getName() const { return "Derived"; } }; int main() { Derived derived; Base &rBase{ derived }; std::cout << "rBase is a " << rBase.getName() << '\n'; return 0; } |
This example prints the result:
rBase is a Base
Because rBase is a Base reference, it calls Base::getName(), even though it’s actually referencing the Base portion of a Derived object.
In this lesson, we will show how to address this issue using virtual functions.
Virtual functions and polymorphism
A virtual function is a special type of function that, when called, resolves to the most-derived version of the function that exists between the base and derived class. This capability is known as polymorphism. A derived function is considered a match if it has the same signature (name, parameter types, and whether it is const) and return type as the base version of the function. Such functions are called overrides.
To make a function virtual, simply place the “virtual” keyword before the function declaration.
Here’s the above example with a virtual function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include <iostream> #include <string_view> class Base { public: virtual std::string_view getName() const { return "Base"; } // note addition of virtual keyword }; class Derived: public Base { public: virtual std::string_view getName() const { return "Derived"; } }; int main() { Derived derived; Base &rBase{ derived }; std::cout << "rBase is a " << rBase.getName() << '\n'; return 0; } |
This example prints the result:
rBase is a Derived
Because rBase is a reference to the Base portion of a Derived object, when rBase.getName() is evaluated, it would normally resolve to Base::getName(). However, Base::getName() is virtual, which tells the program to go look and see if there are any more-derived versions of the function available between Base and Derived. In this case, it will resolve to Derived::getName()!
Let’s take a look at a slightly more complex example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#include <iostream> #include <string_view> class A { public: virtual std::string_view getName() const { return "A"; } }; class B: public A { public: virtual std::string_view getName() const { return "B"; } }; class C: public B { public: virtual std::string_view getName() const { return "C"; } }; class D: public C { public: virtual std::string_view getName() const { return "D"; } }; int main() { C c; A &rBase{ c }; std::cout << "rBase is a " << rBase.getName() << '\n'; return 0; } |
What do you think this program will output?
Let’s look at how this works. First, we instantiate a C class object. rBase is an A reference, which we set to reference the A portion of the C object. Finally, we call rBase.getName(). rBase.getName() evaluates to A::getName(). However, A::getName() is virtual, so the compiler will call the most-derived match between A and C. In this case, that is C::getName(). Note that it will not call D::getName(), because our original object was a C, not a D, so only functions between A and C are considered.
As a result, our program outputs:
rBase is a C
A more complex example
Let’s take another look at the Animal example we were working with in the previous lesson. Here’s the original class, along with some test code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
#include <iostream> #include <string> #include <string_view> class Animal { protected: std::string m_name; // 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(const std::string &name) : m_name{ name } { } public: const std::string &getName() const { return m_name; } std::string_view speak() const { return "???"; } }; class Cat: public Animal { public: Cat(const std::string& name) : Animal{ name } { } std::string_view speak() const { return "Meow"; } }; class Dog: public Animal { public: Dog(const std::string &name) : Animal{ name } { } std::string_view speak() const { return "Woof"; } }; void report(const Animal &animal) { std::cout << animal.getName() << " says " << animal.speak() << '\n'; } int main() { Cat cat{ "Fred" }; Dog dog{ "Garbo" }; report(cat); report(dog); return 0; } |
This prints:
Fred says ??? Garbo says ???
Here’s the equivalent class with the speak() function made virtual:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
#include <iostream> #include <string> #include <string_view> class Animal { protected: std::string m_name; // 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(const std::string &name) : m_name{ name } { } public: const std::string &getName() const { return m_name; } virtual std::string_view speak() const { return "???"; } }; class Cat: public Animal { public: Cat(const std::string &name) : Animal{ name } { } virtual std::string_view speak() const { return "Meow"; } }; class Dog: public Animal { public: Dog(const std::string& name) : Animal{ name } { } virtual std::string_view speak() const { return "Woof"; } }; void report(const Animal &animal) { std::cout << animal.getName() << " says " << animal.speak() << '\n'; } int main() { Cat cat{ "Fred" }; Dog dog{ "Garbo" }; report(cat); report(dog); return 0; } |
This program produces the result:
Fred says Meow Garbo says Woof
It works!
When animal.speak() is evaluated, the program notes that Animal::speak() is a virtual function. In the case where animal is referencing the Animal portion of a Cat object, the program looks at all the classes between Animal and Cat to see if it can find a more derived function. In that case, it finds Cat::speak(). In the case where animal references the Animal portion of a Dog object, the program resolves the function call to Dog::speak().
Note that we didn’t make Animal::getName() virtual. This is because getName() is never overridden in any of the derived classes, therefore there is no need.
Similarly, the following array example now works as expected:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Cat fred{ "Fred" }; Cat misty{ "Misty" }; Cat zeke{ "Zeke" }; Dog garbo{ "Garbo" }; Dog pooky{ "Pooky" }; Dog truffle{ "Truffle" }; // Set up an array of pointers to animals, and set those pointers to our Cat and Dog objects Animal *animals[]{ &fred, &garbo, &misty, &pooky, &truffle, &zeke }; for (const auto *animal : animals) std::cout << animal->getName() << " says " << animal->speak() << '\n'; |
Which produces the result:
Fred says Meow Garbo says Woof Misty says Meow Pooky says Woof Truffle says Woof Zeke says Meow
Even though these two examples only use Cat and Dog, any other classes we derive from Animal would also work with our report() function and animal array without further modification! This is perhaps the biggest benefit of virtual functions -- the ability to structure your code in such a way that newly derived classes will automatically work with the old code without modification!
A word of warning: the signature of the derived class function must exactly match the signature of the base class virtual function in order for the derived class function to be used. If the derived class function has different parameter types, the program will likely still compile fine, but the virtual function will not resolve as intended.
Use of the virtual keyword
If a function is marked as virtual, all matching overrides are also considered virtual, even if they are not explicitly marked as such. However, having the keyword virtual on the derived functions does not hurt, and it serves as a useful reminder that the function is a virtual function rather than a normal one. Consequently, it’s generally a good idea to use the virtual keyword for virtualized functions in derived classes even though it’s not strictly necessary.
Return types of virtual functions
Under normal circumstances, the return type of a virtual function and its override must match. Consider the following example:
1 2 3 4 5 6 7 8 9 10 11 |
class Base { public: virtual int getValue() const { return 5; } }; class Derived: public Base { public: virtual double getValue() const { return 6.78; } }; |
In this case, Derived::getValue() is not considered a matching override for Base::getValue() (it is considered a completely separate function).
Do not call virtual functions from constructors or destructors
Here’s another gotcha that often catches unsuspecting new programmers. You should not call virtual functions from constructors or destructors. Why?
Remember that when a Derived class is created, the Base portion is constructed first. If you were to call a virtual function from the Base constructor, and Derived portion of the class hadn’t even been created yet, it would be unable to call the Derived version of the function because there’s no Derived object for the Derived function to work on. In C++, it will call the Base version instead.
A similar issue exists for destructors. If you call a virtual function in a Base class destructor, it will always resolve to the Base class version of the function, because the Derived portion of the class will already have been destroyed.
Rule: Never call virtual functions from constructors or destructors
The downside of virtual functions
Since most of the time you’ll want your functions to be virtual, why not just make all functions virtual? The answer is because it’s inefficient -- resolving a virtual function call takes longer than resolving a regular one. Furthermore, the compiler also has to allocate an extra pointer for each class object that has one or more virtual functions. We’ll talk about this more in future lessons in this chapter.
Quiz time
1) What do the following programs print? This exercise is meant to be done by inspection, not by compiling the examples with your compiler.
1a)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#include <iostream> #include <string_view> class A { public: virtual std::string_view getName() const { return "A"; } }; class B: public A { public: virtual std::string_view getName() const { return "B"; } }; class C: public B { public: // Note: no getName() function here }; class D: public C { public: virtual std::string_view getName() const { return "D"; } }; int main() { C c; A &rBase{ c }; std::cout << rBase.getName() << '\n'; return 0; } |
1b)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#include <iostream> #include <string_view> class A { public: virtual std::string_view getName() const { return "A"; } }; class B: public A { public: virtual std::string_view getName() const { return "B"; } }; class C: public B { public: virtual std::string_view getName() const { return "C"; } }; class D: public C { public: virtual std::string_view getName() const { return "D"; } }; int main() { C c; B &rBase{ c }; // note: rBase is a B this time std::cout << rBase.getName() << '\n'; return 0; } |
1c)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#include <iostream> #include <string_view> class A { public: // note: no virtual keyword std::string_view getName() const { return "A"; } }; class B: public A { public: virtual std::string_view getName() const { return "B"; } }; class C: public B { public: virtual std::string_view getName() const { return "C"; } }; class D: public C { public: virtual std::string_view getName() const { return "D"; } }; int main() { C c; A &rBase{ c }; std::cout << rBase.getName() << '\n'; return 0; } |
1d)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#include <iostream> #include <string_view> class A { public: virtual std::string_view getName() const { return "A"; } }; class B: public A { public: // note: no virtual keyword in B, C, and D std::string_view getName() const { return "B"; } }; class C: public B { public: std::string_view getName() const { return "C"; } }; class D: public C { public: std::string_view getName() const { return "D"; } }; int main() { C c; B &rBase{ c }; // note: rBase is a B this time std::cout << rBase.getName() << '\n'; return 0; } |
1e)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#include <iostream> #include <string_view> class A { public: virtual std::string_view getName() const { return "A"; } }; class B: public A { public: // Note: Functions in B, C, and D are non-const. virtual std::string_view getName() { return "B"; } }; class C: public B { public: virtual std::string_view getName() { return "C"; } }; class D: public C { public: virtual std::string_view getName() { return "D"; } }; int main() { C c; A &rBase{ c }; std::cout << rBase.getName() << '\n'; return 0; } |
1f)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#include <iostream> #include <string_view> class A { public: A() { std::cout << getName(); } // note addition of constructor virtual std::string_view getName() const { return "A"; } }; class B : public A { public: virtual std::string_view getName() const { return "B"; } }; class C : public B { public: virtual std::string_view getName() const { return "C"; } }; class D : public C { public: virtual std::string_view getName() const { return "D"; } }; int main() { C c; return 0; } |
![]() |
![]() |
![]() |
For the last example F,
1. Does it work like this?
A() //getName() will return "A";
B()
C()
~C()
~B()
~A()
2. Could we use the same example for consistency between examples? Example F.
If I add the above code into example F, I will get "AC", instead of just "A".
This will let students know that calling from a constructor just creates an additional output.
1. Feedback
"The word virtual means "may be redefined later in a class derived from this one.""
Maybe we could add the definition into the tutorial?
That's not quite what it means: functions can be redefined in derived classes without being virtual.
Maybe the C++20 manual might need a slightly longer definition?
Happy New Year, Alex!
>>because the Derived portion of the class will already have been destroyed.
I think 'will' should be removed. Don't you think?
>>which tells the program to go look and see if there are any more-derived versions of the function available.
>> the program looks at all the classes between Animal and Cat to see if it can find a more derived function
shouldn't they be 'most-derived'?
I also kept getting this warning 'Using uninitialized memory ' because of the following code. I don't know why as it seems to be initialized.
Severity Code Description Project File Line Suppression State
Warning C6001 Using uninitialized memory '*myObject'. ConsoleApplication2 C:\Users\Tim\source\repos\ConsoleApplication2\ConsoleApplication2.cpp 28
"more" is correct. There's nothing wrong with your code, all variables are initialized.
Thanks!
I noticed Code::Blocks will -Werror if there is no virtual destructors in each class.
Either add virtual ~A(){}, virtual ~B(){} and virtual ~C(){} OR remove -Werror in compiler settings.
Not sure if things should be this way since this chapter was updated kind of recently.
Virtual destructors are covered in the next lesson. The quizzes in this lesson don't require the readers to compile anything, so warnings are acceptable.
You only need a virtual destructor in the base class, all children get a virtual destructor automatically if the base has one. To do so, use `default`
There is no getName() function in class A. Do how does the line 7 execute?
in the last quiz?
Please explain in details.
in 1a the getname() function would still be inherited in C from B and you already wrote that it is not necessary to write virtual keyword in derived class.so i think the function call should resolve to C::getname()
Hello fellow driver,
the question is which function the call ultimately resolves to. It goes through `C::getName`, but `C` doesn't have its own `getName` function, so the call passes through to `B::getName`.
I find quiz 1f somewhat confusing.Isn't our point not to call any virtual functions in the constructor?I mean the object A is not yet made during the construction.How can it see the function getName()?
This quiz question is designed to reinforce that you shouldn't call virtual functions in the constructor. Object A is created prior to the start of the constructor running. It can see function getName() because member function A::getName() is in class A's declaration.
Hey A and N,
I find quizz question 1.d) to be a bit misleading : the note besides the overridden versions of the function says "// note: not virtual", but the solution states that "B::getName() and C::getName() are considered implicitly virtual".
Maybe it would be better to replace the note by "// note: no virtual keyword here", because simply "not virtual" is untrue.
Conceptually, a derived class is made of the base class and all the parts that are in its parent hierarchy.
Does this mean space is allocated for the 'methods' too? I tried it and extra space doesn't seem to be allocated for normal class methods, but only for virtual methods. By this, I'm guessing virtual functions resolved at runtime. This also must mean that to be performant, they must ideally have very little code.
Space is not allocated for methods, as those get turned into code that doesn't require per-object storage.
If the class has virtual functions, a virtual pointer is added to each object, which increases its size slightly. This is a fixed cost per object (one pointer) regardless of how many virtual functions exist. This is covered in the upcoming lesson on virtual tables.
Yes yes I noticed that! I added 3 virtual functions, and it didn't seem to add any extra size apart from the very first one! Is this pointer same for all objects that have the same base? So virtual functions are resolved at runtime?
Thank you!
All of this is explained in upcoming lessons. If it's still not clear after reading, ask again on the lessons directly related to these topics.
Thank you very much for your help! Finished those lessons yesterday!
Here too the main's return statement is missing in the very first snippet and in the Animal examples in the paragraph 'A more complex example'.
Hi Alex,
couple of typos i noticed:
1)"...__rBase.GetName()__ evaluates to A::getName()..." should be rBase.getName() ('g' not 'G').
2) "Note that we didn’t make __Animal::GetName()__ virtual. This is because __GetName()__ is..." same as above.
thanks for this wonderful treasure.
Thanks for pointing out the inconsistencies. They have been fixed.
Hi,
As this is my first post, I'd like to take the opportunity to thank you for your excellent tutorials.
Now, for what is very likely a stupid question. I must have missed something very basic somewhere, but I am confused by the following:
Function report takes a reference to an Animal, but is later called on objects of a different type (Cat and Dog). I would have expected an error, but it works. This is the first time (I think) I have seen this done in the tutorials, or have I missed something?
Why is it that the above is ok, but the following is not?
Hi John!
@Cat and @Dog inherit from @Animal. They can be used everywhere where an @Animal pointer or reference is used. Chapter 11 (Inheritance).
double and int are different types, they can't do this.
Many thanks for prompt reply. It was a stupid question after all - it's also covered in 12.1.
Do you mean they get implicitly casted into a base object?
No, they don't get cast. `animal` in `report` is a reference, base references can bind to derived objects. If they were cast, they'd get sliced (Covered later), that'd be bad.
Hello,
could you explain how to fix this diamond problem? Thanks!
See this : https://www.cprogramming.com/tutorial/virtual_inheritance.html
so to solve the ambiguity where the compiler cant decide on more than one copy of the virtual functions as in your case, use public virtual derivation instead of only pubic. By doing this the functions derived from base class is deducted to only single copy.
#include <string>
#include <iostream>
class LivingThing {
public:
virtual void breathe() {
std::cout << "I'm breathing as a living thing." << std::endl;
}
virtual void crawl()
{
std::cout << "I'm crawling as an Living Thing." << std::endl;
}
};
class Animal : public virtual LivingThing {
public:
void breathe() {
std::cout << "I'm breathing as an animal." << std::endl;
}
void crawl()
{
std::cout << "I'm crawling as an Animal." << std::endl;
}
};
class Reptile : public virtual LivingThing {
public:
void breathe() {
std::cout << "I'm breathing as a reptile." << std::endl;
}
void crawl() {
std::cout << "I'm crawling as a reptile." << std::endl;
}
};
class Snake : public Animal, public Reptile {
public:
void breathe() {
std::cout << "I'm breathing as a Snake." << std::endl;
}
void crawl() {
std::cout << "I'm crawling as a Snake." << std::endl;
}
};
int main() {
Snake snake;
LivingThing* L = &snake;
L->breathe();
L->crawl();
return 0;
}
Hi Alex,
A quick question regarding Quiz 1(e):
You said that the getName() functions in the classes B, C and D do not override the corresponding function in the class A. Then it seems that the classes B, C and D have two versions of getName(), so what would be the result of the following code?
And more confusingly, what would be the result of the following code?
You can run these yourself and see. :)
The top one invokes c.getName(), which resolves to C::getName() and prints 'C'.
The bottom one invokes rBase.getName(), which would normally invoke B::getName(). But that function is virtual, and rBase is pointed at an object of type C, so C's override is used. Thus C::getName() is called, and prints 'C'.
My question is then, why isn't A::getName() called? It is not overridden by anything and has the right signature, and so should be inherited by B, C and D.
Good question. Because C::getName() shadows (hides) A::getName(), so the compiler won't see A::getName().
Where can I read about the "shadowing" mechanism?
Look up shadowing in the site index.
It seems that what shadows A::getname() is B::getname(), because if I comment out the definition of B::getname()
the program outputs "A" instead.
Hey Alex,
the "override" keyword introduced with C++11 is not mentioned in this chapter. In some literature, like the Mozilla portability guide, the following syntax is stated to be preferred:
Maybe you want to add a remark about that, whichever way that'll go. It might be a little clearer, but it's also another keyword and structure to remember.
Hi DecSco!
override is covered in the next chapter (12.2a).
Hi Alex, There is no const in return type of derived class but it still considered override. But if const is not present in base but it is present in derived i get compiler error.
class Base
{
public:
virtual const char* getName() { return "Base"; }
};
class Derived: public Base
{
public:
char* getName() { return "Derived"; } // no const in return type
};
int main()
{
Derived derived;
Base &rBase = derived;
std::cout << "rBase is a " << rBase.getName() << '\n';
}
Seems like a bug in your compiler. Visual Studio 2017 emits an error in both cases.
Hi Alex,
I don't get what you mean in 1d): "A::getName() is virtual and B::getName() and C::getName() are overrides"
I thought B::getName() and C::getName() are not virtual, as a result, it would print B instead of C.
Thanks in advance.
From the lesson: "Only the most base class function needs to be tagged as virtual for all of the derived functions to work virtually."
Even thought B::getName() and C::getName() aren't marked as virtual, because they are proper overrides to A::getName(), which IS virtual, they are also considered virtual.
I'll make this more clear in the answer.
Dear Alex,
I think I have a problem related to what you explained here but a bit more complicated.
Here is my code:
#include <iostream>
using namespace std;
class Base
{
public:
virtual int foo(int i) = 0;
virtual ~Base(){};
};
class A
{
public:
virtual double foo(int i)
{
cout << "A::foo()" << endl;
return 0.0;
}
virtual ~A(){};
};
class B: virtual public Base, public A
{
public:
int foo(int i)
{
cout << "B::foo()" << endl;
return 0;
}
virtual ~B(){};
};
int main()
{
A a;
B b;
Base* ba = new B();
a.foo(1);
b.foo(1);
ba->foo(1);
delete ba;
return 0;
}
I would like class B::foo to be an implementation of class Base::foo and not of class A::foo.
How can I do that?
Many thanks,
- Mauro.
I'm not sure I understand what you're asking. B::foo isn't a class, it's a member function. Your program also doesn't even compile on Visual Studio 2017 because A::foo() returns a double and B::foo() is an override that tries to change the return type.
Hi Alex !
In question 1)b I just made a slight change I made access specifier of class C private
So when we call rBase.getName(), C::getName() will get called since it is most derived.
Since we have made the access specifier of class C private, it should throw us an compile error, But this code compiles harmlessly.What am I missing ?
rBase is a reference of type B, therefore access to members through that reference is goverened by the access controls of class B (regardless of whether that reference is actually pointing to a B, C, D, or anything else).
This is because access controls are checked at compile time, and the compiler has no idea what it might be actually pointing to (that's only known at runtime).
in base class if you make the function virtual compiler will throw the error , but in the derrived class if we try to make the virtual function private it will call this private function from derived class.
I think this is the HACK!! ...We are able to access the method which are private in derived class from pointer / reference of the base class.
Kindly suggest Alex!!!
But we can access the private virtual method by making the main() as friend of base class
const char* getName() { return "B"; }
here, u define a function return a pointer, but why it can be directly used as
rBase.getName() and return B? rather than using
*(rBase.getName())?
Hi ben!
The return value of @getName isn't just a pointer, it's an array of characters forming a string. If you were to dereference the return value you'd end up with only the first character of the string (Which is equivalent to the entire string in this case).
References
Lesson 6.6 - C-style strings
For 12.2.B, I can not understand the explanation. Can someone help?
rBase is in the scope of B. Its definition is B &rBase.
When the call (rBase.getName) is executed, I thought the relation is
between the base class(A) and the class B, so I gave a solution of B,
which is wrong.
However, "the most derived matching call" is between classes B and C,
which I do not understand.
When you write a virtual function, you want to let the "old" code to use the "new" code.
Virtural function is designed to tigger this mechanism.
In this question, the connection between "old" and "new" is via a reference, so you only need to consider the relation between these two layers of the inheritance.
In this definition, there was no mention of const in class Base but still is considered overridden in class Derived as opposed to Problem 1e). Can you explain what is the difference or am I missing something ?
Thanks in advance.
The const-ness of a member function is considered part of its signature, so a const function will not be considered an override for a non-const function, or vice-versa, even if all of the other parameters match.
The point of 1e is to demonstrate this -- the base class has a const member, whereas the derived classes do not. Therefore, the derived class versions are not considered overrides.
However, in this case, both the Base and Derived versions of getName() are non-const, and thus the Derived version is considered an override.
Because rBase is a Base reference, it calls Base::getName(), even though it’s actually referencing the Base portion of a Derived object. <- Looks like wrong statement.
Because rBase is a Base reference, it calls Base::getName(), even though it’s actually referencing the Derived portion of a Derived object. <- That's correct?
No, it's correct as written. The Base reference is referencing the Base portion of a Derived object. The Base reference doesn't even know there's a Derived portion of the Derived object.
The answer to Question 1e) is not correct. B::getName() and c::getName() are const. Maybe there is a typo and const should be removed for B::getName() and c::getName(). Thanks!
No, b::GetName() and c::getName() return a const char* value, they aren't const themselves. If a member function is const, the const comes after the function name.
Why do virtual functions work for pointers and references but not copies?
Because pointers and references can point to the base portion of an inherited object. If you try to copy the base class of an inherited object, you get object slicing, where the base class is copied, but the derived class(es) are not, leading to a situation where virtual functions don't work because the derived portion of the class has been lost. I cover object slicing later in the chapter.
Hi Alex
I do not understand why virtual is useful.
I mean. If you have a base variable, the main interest is not to call the getName function inside base instead of derived?
For me does not make any sense.
It allows us to write a single report() function that can take any kind of Animal, rather than having to write separate versions for each Class.
Later on, we'll also see how it allows us to create Arrays that can hold any kind of Animal.