I have read that it is not good to overuse inheritance in C++.
I have a simple example where I would like to know if it is good or bad to use it to initialize values in the base class constructor.
Let's say I have a class Animal :
class Animal{
protected :
Animal(std::string name, std::string category);
std::string m_name;
std::string m_category;
};
Is it good to create a specific class for each animal the application is going to use for example :
class Bear : public Animal{
public :
Bear():Animal("Bear", "Mammal"){
}
};
Of course that means that if I have 100 animals, I need 100 classes, and that feels awkward to my eyes.
In this example, I am using inheritance only to initialize values, there won't be any specific virtual methods involved.
Thanks
Broadly speaking, the choice of whether to create derived classes or to just have data members on one class becomes a question of whether more classes actually help with the situation you're trying model/solve in the software.
For instance, an application that cares about how vehicles travel might have different classes for Car, Boat, Airplane, Helicopter, etc. On the other hand an application that only cares about the existence of vehicles (say it's for tracking what a business owns) might only need a single class for Vehicle in general.
As one rule of thumb, ask yourself if the derived classes are actually going to behave differently in any way, as far as what the rest of your program is concerned with. If yes, then derived classes may be what you want. If no, then probably not.
In your specific case, is there going to be any real difference between
Bear bear;
and
Animal bear("Bear", "Mammal");
If not, then a derived class of each type of Animal sounds excessive (100 derived classes sounds excessive in general) and you probably want to do something else depending on how you want to store the data. Perhaps making a list or map of Animal instances.
Usually, it's not a good practice to have members in the base class. Rather than that, create an interface and provide an implementation in the derived class returning the values:
class Animal {
protected:
virtual std::string getName() const;
virtual std::string getCategory() const;
};
class Bear : public Animal{
public:
virtual std::string getName() const override {
return std::string("Bear");
}
virtual std::string getCategory() const override {
return std::string("Mamal");
}
};
Inheritance is useful when you have different cases of implementation but still have the same working base behind. In your case if the only difference between all your animals is the name and category and that there's no specific implementations then I personally think this is not useful.
However what could be improved in your case is the need of 2 arguments that define the same thing (a Bear is always a mammal for example). Maybe a map could fix this issue in your case?
Making a class for each animal seems excessive. I did a school work before where I used an Animal class and I made the derived class into Predator and Prey. So, make the derived ones be specific types of animal, like Mammal, Fishes, etc. For your example, you can just use the single Animal class and call that Animal bear.
Related
I'm having a rough time with a particular C++ inheritance problem. Say we have two abstract classes, one using the other as argument type for one of the pure virtual functions:
class Food {
public:
int calories=0;
virtual void set_calories(int cal)=0;
}
class Animal {
public:
int eaten_calories=0;
virtual void eat_food(Food &f)=0;
}
Now, we create a derived class for each, and we instantiate a virtual function with arguments of type the derived class:
class Vegetables: public Food{
public:
void set_calories(int cal){calories=cal;}
}
class Cow: public Animal{
public:
void eat_food(Vegetables &v){this->eaten_calories += v.calories;}
}
The problem with this is that the function eat_food requires a signature with the abstract class Food, or else a Cow() object creation won't compile, complaining that Cow is an abstract class because no suitable implementation of eat_food(Food f) was found.
Update: An additional constraint I seek for the implementation is that a second class Meat: public Food should not be usable with Cow::eat_food(f). In short, just setting Cow::eat_food(Food f) and casting to Vegetables wouldn't cut it.
What is the best way to overcome this error?
So far I have found two options:
Creating an eat_food(Food f) implementation in Cow with a try/catch to check if f can be safely casted to Vegetables, and then calling eat_food(Vegetables v). PROBLEM: if you have 50 virtual functions, this forces you to write 50 additional function implementations in Cow.
Turn the Animal into a Template class Animal<T>, and instantiate it with each of the derived classes of Food to define the animals (e.g., class Cow: public Animal<Vegetables>). PROBLEM: you can no longer define an Animal* pointer to hold an undefined animal with not known type.
Is there any viable/stylish alternative to these two? Maybe a software pattern of some kind?
When you defined the virtual function Animal::eat_food() accepting a Food& parameter, you declared that for any Animal, you can provide any Food to eat_food(). Now you want to break that promise. This brings into question your design. Either it is legitimate to call ptr->eat_food(food) where ptr is an Animal* and food is a Meat, or eat_food() should (probably) not be defined in the Animal class. If you cannot substitute one Food for another, use of Food& is likely a mistake. If you cannot substitute one Animal for another, defining at the Animal level is likely a mistake.
Perhaps a small change in nomenclature would help this make more sense. Consider renaming eat_food to give_food, or perhaps feed. Now you have a concept that is applicable to all animals. You can feed any food to any animal, but whether or not the animal eats it is a different story. Maybe you should make your virtual function feed() so that it applies equally well to all animals. If you have an Animal* and a Food&, you can feed the animal, but it's the animal that decides if it eats. If you were to instead insist that you must know the correct type of Food before feeding the Animal, then you should have a Cow* instead of an Animal*.
Note: If you happen to be in a case where you never try to feed an Animal*, then you could remove the virtual function from Animal, in which case your question becomes moot.
This might look something like the following.
class Animal {
int eaten_calories=0;
protected:
void eat_food(Food &f) { eaten_calories += f.calories; } // Not virtual
public:
virtual void feed(Food &f)=0;
};
class Cow: public Animal{
public:
void feed(Food &f) override {
// Cows only eat Vegetables.
if ( dynamic_cast<Vegetables*>(&f) ) // if `f` is a Vegetables
eat_food(f);
else
stampede(); // Or whatever you think is amusing (or appropriate).
}
};
Note that I have kept your eat_food() implementation, but moved it to a non-virtual function in Animal. This is based on an assumption, so it might be inappropriate. However, I am willing to assume that no matter what type of animal, and no matter what type of food, if the animal actually eats the food, then the eaten calories should increase by the food's calories.
In addition, a rule of thumb says that this might be the correct level of abstraction -- the two bits of data being used, calories and eaten_calories, belong directly to the two classes being used, Animal and Food. This suggests (just a rule of thumb) that your logic and data are at a consistent level of abstraction.
Oh, I also specified protected access for eat_food(). This way it is the object's decision whether or not to eat. No one will be able to force an animal to eat; they would only be able to offer it food. This demonstrates another principle of polymorphic design: when derived classes differ, only the objects of those classes should need to be aware of those differences. Code that sees only objects of a common base should not need to test for these differences in advance of using those objects.
If you pass around a polymorphic type (like Vegetables) as a base type by value (like Food f), you will slice the object, which prevents overriden methods from being called.
You need to pass such types by pointer or by reference instead, eg:
class Food {
public:
virtual int get_calories() const = 0;
};
class Animal {
public:
int eaten_calories = 0;
virtual void eat_food(Food& f) = 0;
};
class Vegetables: public Food {
public:
int get_calories() const { return ...; }
};
class Cow: public Animal{
public:
void eat_food(Food& f){ this->eaten_calories += f.get_calories(); }
};
Vegetables veggies;
Cow cow;
cow.eat_food(veggies);
UPDATE:
You can't change the signature of a virtual method in derived classes (except when using covariant return types). Since eat_food() is exposed in Animal and takes a Food&, if you want Cow::eat_food() to accept only a Vegetables object and not a Meat object, then it needs to check at runtime if the input Food& refers to a Vegetables object and if not then throw an exception. dynamic_cast does exactly that for you when casting a reference, eg:
class Cow: public Animal{
public:
void eat_food(Food& f){ this->eaten_calories += dynamic_cast<Vegetables&>(f).calories; }
};
Vegetables veggies;
Meat meat;
Cow cow;
cow.eat_food(veggies); // OK
cow.eat_food(meat); // throws std::bad_cast
Suppose I have this base class:
struct Vehicle {
//"op" stands for the operator of the vehicle
//examples: pilot, truck driver, etc.
virtual void insert_op(Op op) = 0;
//other members...
};
And these two subclasses
struct Truck : public Vehicle {
void insert_op(Op op) override {
//prepare Truck with truck driver
}
};
struct Airplaine : public Vehicle {
void insert_op(Op op) override {
//prepare Airplaine with pilot
}
};
As you guess, this is the other hierarchy:
struct Op {};
struct TruckDriver : public Op {};
struct Pilot : public Op {};
You already see the problem, don't you? I want to FORCE Truck to accept only TruckDrivers and FORCE airplaines to accept only Pilots, but this is not possible in the current design. C++ does not allow diff parameters for overridden virtuals.
I guess I could do a run-time type check of the type of "Op" in each of the subclass implementations of insert_op, but that sounds like a really ugly solution, plus its not enforced at compile time.
Any ways out?
Your Vehicle says virtual void insert_op(Op op) which means "every vehicle can accept any Op".
Therefore, according to your design, a Truck isn't a valid candidate for a Vehicle subclass because it can't accept any Op - it can accept only TruckDrivers.
Related: Liskov substitution principle
The problem is in your design, not in implementation of it. I suggest to simplify your class hierarchy. Do you really need so many classes and inheritance? Can you simply go with Vehicle and Op that have fields identifying their type?
Let me further explain the design problem:
Assume some object A with a method manVehicle(Vehicle&). Truck is a subclass of Vehicle, so it's possible to call this method with an object of type Truck.
However, the implementation of A doesn't have a clue what concrete types of Vehicles. It only knows that all vehicles have a method insert_op(Op), so it's valid for it to attempt a call like insert_op(Pilot()) even if the vehicle is actually a Truck.
Conclusions:
Compile-time check isn't even possible
Runtime check could work...
but would only sweep the problem under the rug. An user of Vehicles expects to be able to call insert_op(Op) on any Vehicle.
A solution would be to modify the Vehicle interface to look like:
struct Vehicle {
virtual bool can_insert_op(Op op) = 0;
virtual void insert_op(Op op) = 0;
};
and document it so that the caller would know that insert_op can be only called with Ops that satisfy can_insert_op on the given Vehicle. Or something analogous (like a documented exception from insert_op "invalid op type for this vehicle") - anything works as long as it's a documented part of this interface.
BTW technical remark: You'd probably want these methods to take the Op by pointer or reference instead of copying it, to avoid an unnecessary copy as well as slicing.
The Vehicle subclasses should each create their their appropriate Operator. There should be no methods to set the operator. Vehicle could have a get_operator method, but that's about it.
I'm guessing this isn't your actual hierarchy (unless you're creating a game or something). If you show your actual hierarchy it might help with suggesting better solutions.
don't find against OOD. if truck driver is a child of driver then its a driver but has other features. in your case the driver is not allowed then its not sub truck driver. If you want to stick with the current design you need to make checks in the start of the fn as you assumed.
polymorphism is designed not to get errors #compile time hence it's dynamic binding at run time not compile time dependent.
What about:
struct Vehicle {
//"op" stands for the operator of the vehicle
//examples: pilot, truck driver, etc.
virtual void insert_op(Op op) = 0;
virtual bool validate_op(Op op);
//other members...
};
struct Truck : public Vehicle {
void insert_op(Op op) override {
if (validate_op(op)) {
//prepare Truck with truck driver
}
}
bool validate_op(Op op) override {
//check if this is a valid truck driver
return ( typeid(op)==typeid(TruckDriver) );
}
};
You would be able to keep the generic definition of insert_op with some validation over it.
What you want to accomplish is legitimate, however, there is no support for a good solution. It is not a problem of C++ itself, but of Object Orientation:
Your class hierarchy starting at Vehicle is covariant with respect to the hierarchy of Op. You'd have the same problem if you had a hierarchy of fuels. Or the nationality of the Ops, etc.
Others have told you explanations of how to accomplish this with run-time checks, but of course, there is always something to be desired.
If you want to accomplish full compile time checking, and the kind of polymorphism you want to have is at compilation time as opposed to runtime, you can use Generic Programming, templates.
I was wondering whether a design pattern or idiom exists to automatically register a class type. Or simpler, can I force a method to get called on a class by simply extending a base class?
For example, say I have a base class Animal and extending classes Tiger and Dog, and I have a helper function that prints out all classes that extend Animal.
So I could have something like:
struct AnimalManager
{
static std::vector<std::string> names;
static void registerAnimal(std::string name) {
//if not already registered
names.push_back(name); }
};
struct Animal
{
virtual std::string name() = 0;
void registerAnimal() { AnimalManager::registerAnimal(name()); }
};
struct Tiger : Animal
{
virtual std::string name() { return "Tiger"; }
};
So basically I would do:
Tiger t;
t.registerAnimal();
This could be worked into a static function as well. Is there any pattern (like a curiously recursive template) or something like that that can help me achieve this without explicitly having to call the registerAnimal method.
I want my class Animal to be extendible in the future and others might forget to call register, I'm looking for ways to prevent that besides documenting this (which I will anyway).
PS This is just an example, I'm not actually implementing animals.
You can indeed do this using the curiously recursive template idiom. It requires nothing from whoever is extending the class that can't be enforced by the compiler:
template<class T>
struct Animal
{
Animal()
{
reg; //force specialization
}
virtual std::string name() = 0;
static bool reg;
static bool init()
{
T t;
AnimalManager::registerAnimal(t.name());
return true;
}
};
template<class T>
bool Animal<T>::reg = Animal<T>::init();
struct Tiger : Animal<Tiger>
{
virtual std::string name() { return "Tiger"; }
};
In this code, you can only extend Animal if you specialize it. The constructor forces the static member reg to be initialized, which in turn calls the register method.
EDIT: As pointed out by #David Hammen in the comments, you won't be able to have a collection of Animal objects. However, this can easily be solved by having a non-template class from which the template inherits and use that as a base class, and only use the template for extending.
If you insist every animal should be registered, why not just make name a parameter of Animal constructor. Then you can put register issues to Animal constructor and every derived will have to pass valid name and register:
struct Animal
{
Animal(std::string name){ AnimalManager::registerAnimal(name);}
}
struct Tiger : Animal
{
Tiger():Animal("Tiger"){}
};
This is a typical example where you want to do some sort of bookkeeping when an object is constructed. Item 9 in Scott Meyers "Effective C++" gives an example of this.
Basically you move all the bookkeeping stuff to base class. Derived class explicitly constructs base class and passes the information that is required for Base class construction. For example:
struct Animal
{
Animal(std::string animal_type)
{
AnimalManager::registerAnimal(animal_type);
};
};
struct Dog : public Animal
{
Dog() : Animal(animal_type()) {};
private:
static std::string animal_type()
{
return "Dog";
};
};
Usually I do this with a macro.
Unit test frameworks often employ the technique for registering the tests, e.g. GoogleTest
#AMCoder: This is not the answer you want. The answer you want, reflection (e.g., what_am_I()) doesn't really exist in C++.
C++ has in a rather limited form via the RTTI. Using RTTI in the base class to determine the "true" type of the object being constructed won't work. Calling typeid(*this) in a base class will instead give you the typeinfo for the class being constructed.
You can make your class Animal have a non-default constructor only. This works fine for classes that derive directly from Animal, but what about classes that derive from a derived class? This approach either precludes further inheritance or requires class builders to create multiple constructors, one of which is for use by derived classes.
You can use Luchian's CRTP solution, but this too has problems with inheritance, and it also precludes you from having a collection of pointers to Animal objects. Add that non-template base class back into the mix so you can have a collection of Animals and you have the original problem all over again. Your documentation will have to say to only use the template to make a class that derives from Animal. What happens if someone doesn't do that?
The easiest solution is the one you don't like: Require that every constructor of a class that derives from Animal must call register_animal(). Say so in your documentation. Show the users of your code some examples. Put a comment in front of that example code, // Every constructor must call register_animal(). People who use your code are going to use cut and paste anyhow, so have some cut-and-paste ready solutions on hand.
Worrying about what happens if people don't read your documentation is a case of premature optimization. Requiring that every class call register_animal() in their constructors is a simple requirement. Everyone can understand it and everyone can easily implement it. You've got much bigger troubles on your hands with your users than a failed registration if your users can't even follow this simple instruction.
You could just call a method in the base class constructor, which will get called everytime a derived class gets instantiated, as follows:
class Animal {
public:
Animal() {doStuff();}
}
The doStuff() method could be implemented in the base class to do static operations, or it could be pure virtual and be implemented in derived classes.
Edit: As correctly pointed out in the comments, virtual methods cant be called in the ctor.
Notice though that the base class constructor will be called before the derived constructors, so you could also do something like this:
class Animal {
public:
Animal(const std::string &name) {doStuff(name);}
private:
Animal(); // Now nobody can call it, no need to implement
}
class Dog : public Animal {
Dog() : Animal("Dog") {}
}
Hope that helps
If you have is-a inheritance relationships implemented with public inheritance, and have a diamond of inheritance you will have something like:
a stream class
input stream and output stream classes derived from stream
an input/output stream class derived from both
In this case, as used in the standard library (?), to the extent that iostream both is-a istream and is-a ostream, the istream isa-a stream and the ostream is-a stream, and furthermore they are the same stream, any functions in stream, which it makes sense to apply to iostream, should deal with the same underlying structure.
In C++, in order that the copies of stream in istream and ostream can be shared, it must be inherited by them virtually.
However, if you prefer, you can not inherit virtually and each time you refer to a member of the base class, specify which of the two copies (one in istream or one in ostream) you want (either by casting, or by using scope::blah).
My question is, [edit: is there any other case where] other than "This isn't really an is-a relationship, I used naughtily used public inheritance as a syntactic convenience when it wasn't conceptually valid" or "I never need to refer polymorphically to the base class from the most-derived class so the incredibly small overhead isn't worth it", there is any reason it WOULD be conceptually valid to inherit non-virtually and have two copies of the base class, one for each sister intermediate class?
Let's work into a simple example.
B B1 B2
| \ /
D D
On the left, we find a class derived from a single bass. Clearly reasonable in OO design for having an is-a relationship respecting the Liskov Substitution Principal.
On the right, both B1 and B2 are independent bases of D, and D can be accessed polymorphically using a B1* or B2* (or references). Hopefully we can accept that this is also a valid OO design (despite Sun deeming it too stressfully mind-bending for Java programmers ;-P). Then consider having some base functionality factored out of both B1 and B2 and made reusable: say they both operate on an arbitrary list of numbers to which they can reasonably grant direct/public access:
Container<int> Container<int>
\ /
B1 B2
\ /
D
If this isn't clicking yet, perhaps:
Container<int> Container<int>
\ /
Ages Heights
\ /
Population
It is reasonable to say that Ages is-a Container<int>, though it might add some convenient functionality like average, min, max, num_teenagers. Same for Heights, possibly sporting a different set of convenience functions. Clearly a Population can reasonably be substituted for an Ages or Heights collection (e.g. size_t num_adults(Ages&); if (num_adults(my_population)) ...).
The point here is that each supporting container isn't meant to have a 1:1 relationship with further-derived classes like Population; rather, it's meant to be exactly 1:1 with its immediately-derived class.
Again, whether composition or inheritance is used is an interface design decision, but it's not necessarily invalid to expose the containers publicly in this way. (If there's concern about maintaining Population invariants such as Ages::empty() == Heights::empty() the data-mutating functions of Container<int> might be made protected while const member functions were public.)
As you observe, Population does not have an unambiguous is-a relationship with Container<int> and code may need explicit disambiguation. That's appropriate. Of course, it's also possible and reasonable for Population to derive from Container<int> if it stores some other set of numbers, but that would be independent of the indirectly inherited containers.
My question is, other than "This isn't really an is-a relationship, I used naughtily used public inheritance as a syntactic convenience when it wasn't conceptually valid"
I see no issue with the is-a relationships or conceptual validity of the above, if you do please explain....
"I never need to refer polymorphically to the base class from the most-derived class so the incredibly small overhead isn't worth it"
I think it's clear I'm just doing data modelling based on natural object relationships, not some dubious optimisation. The supporting container class factored out of the bases is actually used and must be independent in each.
I would say it is more likely to cause havoc.
Let us ignore the case of stateful base class. It seems obvious that having two incoherent states is likely to cause confusion at least, and be error prone.
I would rather focus on the issue of identity. In C++, the identity of an object is determined by its address. This is the very reason why, apart from empty base classes, each object must at least have a size of one byte.
If you have the same base class multiple times in your hierarchy, then you could obtain two Base* that refer to the same object... and yet differ (point to a difference address).
Of course, you could pull your tricks and use a dynamic_cast<void*>(p) to get the "real" physical address of the whole object... but still.
The problem is one of identity; in the case of the iostream
hierarchy, the shared bases even have state, which gets set by
manipulators. If there were more than one instance of
basic_ios, you'ld have two copies of the state (formatting
flags, error state, even the streambuf), which would be
a disaster.
I can't really think of a case where you'ld want two copies of
a base class, but I imagine that they do exist.
An example
class Category
{
public:
virtual std::string CatName();
};
class OxygenBreath : public Category
{
public:
virtual void Breath() = 0;
std::string CatName(){ return "OxygenBreath";}
};
class LandWalk : public Category
{
public:
virtual void Walk() = 0;
std::string CatName(){ return "LandWalk";}
};
class Human : public OxygenBreath, public LandWalk
{};
There might be cases where this is useful. Say a touchscreen class deriving from display_device and input_device? Now suppose each base the common base class has an estimated_power_consumption field, wouldn't it be useful to avoid virtual inheritance?
struct managed
{
report(std::string what);
private:
manager_type manager;
protected:
managed(manager_type);
};
struct human : managed
{
human() : managed(god) {}
};
struct robot : managed
{
robot(manager_type owner) : managed(owner) {}
};
struct employee : managed
{
employee(manager_type boss) : managed(boss) {}
};
struct human_employee : human, employee
{
human_employee(manager_type boss) : employee(boss) {}
};
struct robot_employee : robot, employee
{
robot_employee(manager_type owner) : robot(owner), employee(owner) {}
};
Now, consider:
void do_some_duty(const employee& e)
{
e.do_some_tasks();
e.report("done");
}
void face_disaster(const human& h)
{
h.report("Oh my!");
}
void commit_suicide(const managed& m)
{
m.report("I want to suicide");
}
and as a human employee, the one you report to is different if you're working of not:
human_employee h;
if (h.at_work()) commit_suicide(static_cast<const employee&>(h));
else commit_suicide(static_cast<const human&>(h));
I would even consider using such a design if I really needed to. You can imagine in lieu of managed some class which would store a reference to a global manager object for eg. garbage collection. In this case it really makes sense to have distinct base classes for base objects.
Theoretically speaking, I can imagine a reason to inherit "sisters" B and C from common A non-virtually privately , then inherit publicaly some "compound class" D from A and B.
It is, of course, not an is_a relationshipn between most derived (D) and most base class (A) as private inheritance does not exhibit is_a relationship to users and furher descendants.
However, if B and C sister classes are not designed especially for this case (i.e for diamond inheritance), they may be publicaly inherited from A.
The effect is almost the same as for private inheritance: we can't access A members from D ([edited:at least without explicit casting to sisters; ] any attempt will be ambiguous).
So one may treat public nonvirtual inheritance as a replacement for private nonvirtual inheritance.
But I would never do such tricks.
Iheritance itself is a useful metaphor but not a basement of the universe, and using it subtle features leads to unnecessary complicated design.
IMO, if a replacement of simple(nonvirtual) inheritance with virtual one is (conceptually) unacceptable, there is almost surely a design flaw.
So I would answer "no" (despite of my "theretically speaking..." construction :)
Recently I was given a task where I had to implement something similar to the following:
There are some animals with certain attributes, such as:
Dog1: name: tery, color:white, fav drink: grape juice
Dog2: name: chiva, color:black, fav drink: lemonade
Bird1: name: tweety, canfly: yes, cansing: no
Bird2: name: parry, canfly: no, cansing: yes
How would you do this in C++ efficiently using OOP prractices?
I did something like this:
class Animal {
Animal(...);
...
public String getName() const;
public void setName(string s);
...
private:
String name;
}
class Bird : public Animal {
Bird(...);
public bool canFly() const;
public void setCanFly(bool b);
...
private:
bool canFly;
bool canSing;
}
class Dog : public Animal {
...
}
The problem with this implementation is that i cannot benefit from polymorhism :
Animal* p = new Anima(...);
...
p->canFly();
and I have to use casting:
((Bird*)p)->canFly();
At the end I was criticized of not using virtual functions in base class, and using casts instead of OOP.
But in my opinion it doesn't make sense to use virtual functions here because getName() should be in the base class to avoid multiple implementations of same method. And canFly is not a valid property for dogs for example.
Then I would have to define something absurd like this for each other (future) animal that also inherits from the base class, which would create unnecessary overhead:
bool Dog::canFly () const {
return false;
}
Who is right here, did I not get the basic principles of polymorphism?
Of course 'canFly' is a valid property for a dog, it's just going to return false.
There's no point in having canFly at all if you only implement it when it needs to be true. In your example, by the time you've done your c-style case to a flying animal, then you've already committed to the type of animal, at which point you don't really need to call canFly, because you already know the answer.
If you really don't want to implement canFly in a large number of non-flying animals, then implement virtual bool canFly() const { return false; } in your base class, and just override it in the flying animals.
I'd assume that this is just a contrived 'homework' question, so the answer is bound to look contrived too, but a style which involves lots of casting object types is really going to be bad news in real work.
Well, you don't need a single base class. Consider this:
Animal
|
|--Flying Animal
| |---Bird
|
|--Non Flying Animal
|---Dog
where:
class Animal
{
public:
virtual bool CanFly () = 0;
String Name ();
};
class Flying : public Animal
{
public:
virtual bool CanFly () { return true; }
};
class NonFlying : public Animal
{
public:
virtual bool CanFly () { return false; }
};
class Bird : public Flying
{
};
class Dog : public NonFlying
{
};
There are many other ways to do this as well, even using composition rather than inheritance.
EDIT: Composition. Having a hierarchy where each level in the hierarchy represents a smaller group of members (there are fewer dogs than animals) presents the problem of how to divide the set of all possible types into a hierarchy. As Lagerbaer pointed out in the comments, some birds can't fly.
So instead of creating a complex tree, have a simple tree (or no tree) and have each animal contain a list of characteristics of that animal:
class Animal
{
public:
String Name ();
List <Characteristic> Characteristics ();
};
class Characteristic
{
public:
String Name ();
};
class CanFly : public Characteristic
{
public:
bool CanFly ();
};
class Legs : public Characteristic
{
public:
int NumberOfLegs ();
};
And then, to create a dog:
Animal *CreateDog ()
{
Animal *dog = new Animal;
dog->Characteristics ()->Add (new CanFly (false));
dog->Characteristics ()->Add (new NumberOfLegs (4));
return dog;
}
and to create a bird:
Animal *CreateBird ()
{
Animal *bird = new Animal;
bird->Characteristics ()->Add (new CanFly (true));
bird->Characteristics ()->Add (new NumberOfLegs (2));
return bird;
}
There are two advantages to this:
You can extend it to add whatever characteristics you want.
You can drive the creation of animals from data rather than hard coding it all.
If your language of choice supports reflection, then searching the characteristics list is very straightforward. In non-reflection languages, you'll need to implement some way to identify what each characteristic is.
To address the technical issue, this is wrong:
((Bird*)p)->canFly();
This C-style cast performs a static_cast; if p points to a Dog, the cast will succeed but the result will be incorrect. Bad Things Happen.
When you don't know the most derived type of the pointed-to object and you don't have some way of determining its type via the base class pointer, you need to use dynamic_cast:
if (Bird* bp = dynamic_cast<Bird*>(p)) {
// p points to a Bird
}
else {
// p points to something else
}
dynamic_cast returns a null pointer if the cast fails, allowing you to check the type of the object.
To address the design issue, it depends. In real-world software you can't always have virtual functions in the base class that define the behavior of every possible derived class. It's just not possible. Sometimes it is necessary to dynamic_cast to a derived class to be able to call functions not declared in the base class.
Casts probably were not necessary in this very simple case, but if you were to consider a more complete class hierarchy defining the animal kingdom, you'd find that you would either need an enormous number of functions in the Animal base class or you would have to use casts at some point.
Virtual methods only make sense where there is a need for subclasses to provide their own implementation, or to force them to (pure virtual).
In the case of your canFly and canSing usage, where data members in the base class support invariant implementation in all subclasses, making those get/set methods virtual makes no sense at all to me.
A better candidate for virtual would be the corresponding fly and sing methods, where base class implementation might throw and only when the properties are set true would an animal-specific implementation be provided in a subclass.
struct Animal {
std::string name;
std::string favoriteDrink;
bool canFly;
bool canSing;
};
Feel free to wrap get/setters around the members if it makes you happy.
But one thing people tend to forget is that polymorphism is about behavior. It is about making different classes that look the same, but behave differently.
In this example, there is no different behavior between any of the animals, and so making more than one class is overkill.
There is no actual behavior required for any of the animals. The only operations we need are the ability to ask "what is its name", "can it fly", "can it sing" (and of course, "will it blend?")
All of these operations make as much sense for a penguin as they do on a terrier, a blue whale or a shrew. The behavior is the same, only the data changes. And so it should be one class, with different instances for different animals.
And so trying to split them into separate classes goes against all the goals of OOP: you end up intentionally duplicating code, doing less code reuse, and you're making your code less polymorphic, not more. In my solution, any animal is a drop-in replacement for any other animal. Once you start messing about with different classes and virtual methods, you have to actually write new code for each new animal in order for it to be a suitable implementation of the Animal base class.
If you ever need to add the actual Fly() method, you might need different classes. The mechanics of flying are different for a sparrow, an eagle and a bat (although even this depends on the objective. Depending on what abstraction level the application is working on, the "fly" routine might consist of nothing more than setting another bool flag somewhere, or perhaps giving the animal a positive non-zero altitude, in which case the same implementation is reusable for any flying animal).
But at the moment, all we need is the ability to ask whether or not an animal can fly. And the implementation of that is trivially reusable.
But of course, it's clear from the task you were given that the correct answer (where "correct" is defined as "the I expected when I asked the question" is "use lots of virtual methods for everything, and give everything its own class".
Which just goes to show that the more OOP zealotry you get from someone, the lower the odds that they actually understand OOP.
See also my blog post on the topic
It might be too much in that simple case, but later on you could keep all your animals in a linked list (or standard list or array or whatever) and then iterate over all entries and just call the base methods to do all kinds of stuff without having to worry about casts.
Just think of a simple game with GameObject being the base class and the Methods update() and draw() being virtual. You then inherit other classes, e.g. PlayerObject, EnemyObject, PowerUpObject, etc.
In your main loop you could then do something like this:
GameObject *p = firstObject;
while(p)
{
p->update();
p = p->nextObject;
}
This will iterate over all game objects and call the proper update() methods (e.g. moving the player, letting a power up spin or whatever) and you don't have to do some special casting, e.g. checking to see if it's a player or whatever.
I think you are right. Adding every conceivable property that some family of animals can have to a base class Animal is plain silly and produces too much overhead.
Although it is clear what was intended in the task, i.e., that you really have a virtual function canFly in the base class, I think this is poor design.
Declaring something virtual doesn't stop you implementing it in the base class.
It's a mechanism for saying that you should use the most specific implementation available. It is distinct from over-riding the implementation in the derived class.
Why should returning false from canFly() for a dog be a problem? Some birds can't fly and there are non-birds that can fly.
In my humble opinion, having getter and setter methods is indicative of poor object-oriented design. And this problem space is not particularly conducive to showing off what good object-oriented design is either.