I'm learning Object-Oriented Programming in C++ and trying to understand polymorphism with base class pointers.
I created a base class Animal and a derived class Dog, both with a speak() method. I expected that when I create a pointer of type Animal* and assign it to a Dog object, calling speak() would execute the Dog version. However, it always calls the base class version.
How can I achieve that ?
#include <iostream>
using namespace std;
class Animal {
public:
void speak() {
cout << "Animal sound" << endl;
}
};
class Dog : public Animal {
public:
void speak() {
cout << "Woof!" << endl;
}
};
int main() {
Animal* a = new Dog();
a->speak(); // Expected: Woof!, Actual: Animal sound
return 0;
}
You seem to expect that the method that is called will be determined on the actual runtime type of the object it is used with (here a Dog).
This is called runtime (or dynamic) dispatch and it is done in C++ only for virtual methods. In other words, you want Dog::speak to override Animal::speak, only virtual methods can be overidden.
For methods which are not virtual the call is determined at compile time, based on the type of the pointer (in your case Animal*) and not the actual object it points to.
A fixed version:
#include <iostream>
class Animal {
public:
//--vvvvvvv---------------
virtual void speak() {
std::cout << "Animal sound" << std::endl;
}
};
class Dog : public Animal {
public:
//---------------vvvvvvvv--
void speak() override {
std::cout << "Woof!" << std::endl;
}
};
int main() {
Animal* a = new Dog();
a->speak();
}
Output:
Woof!
Live demo - Godbolt
Notes:
I also used the override specifier in the derived class which is recommended, in order to get the help of the compiler when you attempt to override a method that is not eligable for that. But stricktly speaking it is not a must (it is enough that the base method is marked virtual).
a should be deleted, or even better: automatically managed by a smart pointer, e.g. std::unique_ptr:
std::unique_ptr<Animal> a = std::make_unique<Dog>();
a->speak();
// `a` will be deallocated automatically
Note: When deleting via a base class pointer, the base class destructor must also be virtual:
class Animal {
public:
virtual ~Animal() = default; // <-----
virtual void speak() {
std::cout << "Animal sound" << std::endl;
}
};
It is recommended to avoid using namespace std;. See: What's the problem with "using namespace std;"?.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With