I have several classes that inherit from one main class. For the sake of simplicity, I have over-simplified the class definitions to make it short and direct to the point.
class Animal {
protected:
string name;
public:
Animal(string name);
virtual string toString() { return "I am an animal"; }
};
class Bird: public Animal {
private:
bool canFly;
public:
Bird(string name, bool canFly = true)
: Animal(name) // call the super class constructor with its parameter
{
this->canFly = canFly;
}
string toString() { return "I am a bird"; }
};
class Insect: public Animal {
private:
int numberOfLegs;
public:
Insect(string name, int numberOfLegs) : Animal(name) {
this->numberOfLegs = numberOfLegs;
}
string toString() { return "I am an insect."; }
};
Now, I need to declare a vector<Animal> that will hold several instances of each inherited class.
#include <iostream>
#include "animal.h"
#include "bird.h"
#include "insect.h"
// assume that I handled the issue of preventing including a file more than once
// using #ifndef #define and #endif in each header file.
int main() {
vector<Animal> creatures;
creatures.push_back(Bird("duck", true));
creatures.push_back(Bird("penguin", false));
creatures.push_back(Insect("spider", 8));
creatures.push_back(Insect("centipede",44));
// now iterate through the creatures and call their toString()
for(int i=0; i<creatures.size(); i++) {
cout << creatures[i].toString() << endl;
}
}
I expected the following output:
I am a bird
I am a bird
I am an insect
I am an insect
but I got:
I am an animal
I am an animal
I am an animal
I am an animal
I know this has to do with the line 'vector creatures;. It is calling the constructor for Animal. But my intention is to tell the compiler, that this creaturespoints to an array ofAnimalinherited classes, might beBirdmight beinsect, the point is: they all implement their own unique respective version of toString()`.
What can I do to declare a polymorphic array of objects that are inherited from the same ancestor?
You cannot use a value semantic (read about object slicing). You must use pointers.
Example:
#include <iostream>
#include <memory>
#include <string>
#include <vector>
class Animal
{
protected:
std::string name;
public:
Animal(std::string name) : name(name)
{
}
virtual std::string toString()
{
return "I am an animal";
}
virtual ~Animal()
{
}
};
class Bird : public Animal
{
private:
bool canFly;
public:
Bird(std::string name, bool canFly = true) : Animal(name) // call the super class constructor with its parameter
{
this->canFly = canFly;
}
std::string toString()
{
return "I am a bird";
}
};
class Insect : public Animal
{
private:
int numberOfLegs;
public:
Insect(std::string name, int numberOfLegs) : Animal(name)
{
this->numberOfLegs = numberOfLegs;
}
std::string toString()
{
return "I am an insect.";
}
};
int main()
{
std::vector<std::unique_ptr<Animal>> creatures;
creatures.emplace_back(new Bird("duck", true));
creatures.emplace_back(new Bird("penguin", false));
creatures.emplace_back(new Insect("spider", 8));
creatures.emplace_back(new Insect("centipede", 44));
// now iterate through the creatures and call their toString()
for (int i = 0; i < creatures.size(); i++)
{
std::cout << creatures[i]->toString() << std::endl;
}
}
prints:
I am a bird I am a bird I am an insect. I am an insect.
I also recommend reading about Sean parent Run Time Polymorphism. The idea is as follows:
#include <iostream>
#include <memory>
#include <string>
#include <vector>
class Animal
{
public:
struct Interface
{
virtual std::string toString() const = 0;
virtual ~Interface() = default;
};
std::shared_ptr<const Interface> _p;
public:
Animal(Interface* p) : _p(p)
{
}
std::string toString() const
{
return _p->toString();
}
};
class Bird : public Animal::Interface
{
private:
std::string _name;
bool _canFly;
public:
Bird(std::string name, bool canFly = true) : _name(name), _canFly(canFly)
{
}
std::string toString() const override
{
return "I am a bird";
}
};
class Insect : public Animal::Interface
{
private:
std::string _name;
int _numberOfLegs;
public:
Insect(std::string name, int numberOfLegs)
: _name(name), _numberOfLegs(numberOfLegs)
{
}
std::string toString() const override
{
return "I am an insect.";
}
};
int main()
{
std::vector<Animal> creatures;
creatures.emplace_back(new Bird("duck", true));
creatures.emplace_back(new Bird("penguin", false));
creatures.emplace_back(new Insect("spider", 8));
creatures.emplace_back(new Insect("centipede", 44));
// now iterate through the creatures and call their toString()
for (int i = 0; i < creatures.size(); i++)
{
std::cout << creatures[i].toString() << std::endl;
}
}
Problem is with creatures.push_back(Bird("duck", true));
You are creating a Bird object and copying that in the Animal object.
One way is to create objects dynamically so that correct function call can resolve using vtable.
Modify this part of your code and it will work fine.
vector<Animal *> creatures;
creatures.push_back(new Bird("duck", true));
creatures.push_back(new Bird("penguin", false));
creatures.push_back(new Insect("spider", 8));
creatures.push_back(new Insect("centipede",44));
Edit: Make sure to release the memory before creatures goes out of scope.
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