Reputation: 675
I'm attempting to understand how to properly create an inherited class factory in C++. I'm using C++ 14 in this example.
I made a simple example to try and understand how this works:
#include <iostream>
class Animal
{
public:
virtual std::string Sound();
};
class Dog : Animal
{
public:
std::string Sound() {return "Woff!";};
};
class Cat : Animal
{
public:
std::string Sound() {return "Meow!";};
};
class Snake : Animal
{
public:
std::string Sound() {return "Ssss!";};
};
Animal GetAnimal(std::string name)
{
if (name == "Dog")
return Dog();
else if (name == "Cat")
return Cat();
else if (name == "Snake")
return Snake();
return Animal();
}
int main()
{
std::cout << "Sound of a dog: \"" << GetAnimal("Dog").Sound() << "\"\n";
std::cout << "Sound of a cat: \"" << GetAnimal("Cat").Sound() << "\"\n";
std::cout << "Sound of a snake: \"" << GetAnimal("Snake").Sound() << "\"\n";
return 0;
}
Where Dog
, Cat
and Snake
are all inherited classes from Animal
. I want to acquire a local instance of each animal type from the GetAnimal
function, provided the animal name as an argument to that function.
The code above prompts the following error upon a compilation attempt for each one of the return
lines in the GetAnimal
function:
E0269 conversion to inaccessible base class "Animal" is not allowed
Is it not possible to assign an inherited class object to a variable of the inherited class's base type? What would be the proper way of doing something like this in C++?
Thanks for reading my post, any guidance is appreciated.
Upvotes: 1
Views: 91
Reputation: 154027
There are a number of issues here:
private
, which means that the
base class is only visible in the derived class. Any code outside the
derived class will not see it. So an instance of Dog
will not
convert to Animal
outside of member functions of Dog
.Animal
(not a pointer or a reference),
something called slicing occurs: a new instance of Animal
is
created, by copying the Animal
part of the derived class. This
new instance is of type Animal
, however, and virtual functions
in it will resolve to the function in Animal
. Which leads us toAnimal
has a pure virtual function, so is abstract, and cannot be
instantiated. Your code should result in a compiler error when you
try to return Animal
.To correct this, each of these points must be addressed:
public
.const
: in this
case, it really doesn't make any difference, but it's a good idea to
always return const
references unless you intend for the caller to
modify the object).Animal
, so if there is no match, you'll have
to do something else. You could return a pointer, rather than a
reference, and return a null pointer if there is no match for name
,
or you could raise an exception, or even abort (considering no match
as an assertion failure) -- the correct solution here depends on the
application which will be using this code. If no match is an expected
condition, returning a pointer is probably most appropriate. This
would be the case if, for example, name
comes from user input.
Alternatively, if no match is considered something exceptional (say
because name
comes from a file, and no match will only occur if the
file is corrupted), an exception will allow the program to recover,
or at least output an error message with more information. Finally,
if name
has been pre-vetted, and no match is a hard programming
error, aborting may be appropriate. (But it depends on the
application -- in critical applications, it's usual to abort, so that
the back-up will take over, but in applications with significant user
input, you definitely don't want to abort without given the
application the chance to save the data.)Upvotes: 2
Reputation: 117871
You need to use base class pointers or references and also to use public
inheritance. It's usually a good idea to make the base class destructor virtual
too if you plan on destructing the objects through a base class pointer, which is usually what you want to do.
It could look like this:
#include <iostream>
#include <memory>
class Animal {
public:
virtual ~Animal() = default;
virtual std::string Sound() = 0; // needs to be implemented by derived class
};
class Dog : public Animal {
public:
std::string Sound() override { return "Woff!"; };
};
class Cat : public Animal {
public:
std::string Sound() override { return "Meow!"; };
};
class Snake : public Animal {
public:
std::string Sound() override { return "Ssss!"; };
};
std::unique_ptr<Animal> GetAnimal(std::string name) {
if (name == "Dog")
return std::make_unique<Dog>();
else if (name == "Cat")
return std::make_unique<Cat>();
else if (name == "Snake")
return std::make_unique<Snake>();
throw std::runtime_error("Invalid animal");
}
And used like so:
int main() {
std::cout << "Sound of a dog: \"" << GetAnimal("Dog")->Sound() << "\"\n";
std::cout << "Sound of a cat: \"" << GetAnimal("Cat")->Sound() << "\"\n";
std::cout << "Sound of a snake: \"" << GetAnimal("Snake")->Sound() << "\"\n";
}
Output:
Sound of a dog: "Woff!"
Sound of a cat: "Meow!"
Sound of a snake: "Ssss!"
Upvotes: 4