I recently saw an OO design question on some forum and started thinking of using RTTI. However this must be bad design but I am unable to think of an alternative. Here is the simple question :
Create a C++ program for the following scenario using OO concepts -
My dog, named Buddy, lives in the backyard. He barks at night when he sees a cat or a squirrel that has come to visit. If he sees a frog, and he is hungry, he eats it. If he sees a frog and he isn't hungry, he plays with it. If he has eaten 2 frogs already, and is still hungry, he will let it go. If he sees a coyote, he crys for help. Sometime his friend Spot stops by, and they chase each other. If he sees any other animal, he simply watches it. I would expect that you would have an animal class, and a cat, dog, squirrel, coyote class that inherits from the animal class.
I started thinking of having a see() method in the dog class which takes an Animal argument and then checks the actual type of the object (frog, cat etc) and takes the required action - play, chase etc depending on the actual type. However this would require RTTI which must be bad design. Can anybody please suggest a better design which would avoid RTTI and also point out the mistake in my thinking?
There are a ridiculously large number of ways to satisfy this problem using "OO concepts," depending on what you want to emphasize.
Here's the simplest solution that I can come up with:
class Animal {
public:
virtual void seenBy(Buddy&) = 0;
};
class Buddy {
public:
void see(Cat&) { /* ... */ }
void see(Squirrel&) { /* ... */ }
// ...
};
class Cat : public Animal {
public:
virtual seenBy(Buddy& b) { b.see(*this); }
};
class Squirrel : public Animal {
public:
virtual seenBy(Buddy& b) { b.see(*this); }
};
// classes for Frog, Coyote, Spot...
If you need multiple kinds of "perceiving" animals, it's straightforward to make a virtual wrapper for see (producing a form of double dispatch):
// On a parent class
virtual void see(Animal&) = 0;
// On Buddy
virtual void see(Animal& a) { a.seenBy(*this); }
The above requires that the Animal class know something about the Buddy class. If you don't like your methods being passive verbs and want to decouple Animal from Buddy, you can use the visitor pattern:
class Animal {
public:
virtual void visit(Visitor&) = 0;
};
class Cat : public Animal {
public:
virtual void visit(Visitor& v) { v.visit(*this); }
};
class Squirrel : public Animal {
public:
virtual void visit(Visitor& v) { v.visit(*this); }
};
// classes for Frog, Coyote, Spot...
class Visitor {
public:
virtual void visit(Cat&) = 0;
virtual void visit(Squirrel&) = 0;
// ...
};
class BuddyVision : public Visitor {
public:
virtual void visit(Cat&) { /* ... */ }
virtual void visit(Squirrel&) { /* ... */ }
// ...
};
class Buddy {
public:
void see(Animal& a) {
BuddyVision visitor;
a.visit(visitor);
}
};
The second mechanism could be used for purposes other than Buddy seeing an animal (possibly for that animal seeing Buddy). It is, however, more complicated.
Note that OO is definitely not the only way to solve this problem. Other solutions exist that may be more practical for this problem, such as storing the properties of the various animals that cause Buddy to bark, eat, play, etc. This additionally decouples the Buddy class from the Animal class (even the visitor pattern needs an exhaustive list of everything that Buddy can perceive).
The design specifically calls for recognizing certain entities in order to perform certain operations on them. Because there is no rhyme or reason with regard to why certain operations go with certain entities (ie: it's all arbitrary), what you're looking at is either type-based dispatch or property-based dispatch. I'd go with the latter.
Give each entity some set of properties. The dog would thus react based on those properties. Cat and Squirrel would have the property, "Dog should Bark at me." When the Dog encounters an entity with such a property, it would perform the appropriate action.
In this case, an entity is nothing more than the sum of its properties as well as the behaviors based on encountering other entities with various properties. The entity may also have some state associated with it. There would not be a specific Dog or Cat class. There would just be an entity with Cat-like properties and behaviors, and an entity with Dog-like properties and behaviors.
Hint: use virtual functions (on the target animals) instead of RTTI.
Most of the time you can replace RTTI by messaging.
Sort of
Id id = object->send(WHO_ARE_YOU);
switch(id)
{
case ID_FROG: ...; break;
case ID_CAT: ...; break;
}
Messaging is more flexible than RTTI in principle:
other_object->send(IS_SCARRY_OF, this);
as it allows to design relationships that are unknown at the moment. Say tomorrow your dog will see racoon that is defined in some other DLL and yet in Pascal.
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
Currently I'm trying to understand "evilness" of MI. I've just watched a video on youtube where a js guy speaks against inheritance. Here is his example (I've rewrite it in C++):
struct Robot
{ void drive(); };
struct MurderRobot : public Robot
{ void kill(); };
struct CleanerRobot : public Robot
{ void clean(); };
struct Animal
{ void poop(); };
struct Dog : public Animal
{ void bark(); };
struct Cat : public Animal
{ void meow(); };
Then he suggested a new class MurderRobotDog, which, from his point of view, can't be done gracefully by means of inheritance. Surely, it can't be done by means of single inheritance. But I don't see any problem to do that with MI.
I think we could create a base class BarkingObject, which would have all barking stuff. Then the Dog inherits from the Animal, which has common poop(), and from the BarkingObject. And when you need a killing dog-robot, it must inherit from the BarkingObject and the MurderRobot. It makes more sense. The MurderRobotDog can't inherit from a live creature, because then it becomes alive and that contradicts with the definition of a robot. Of course, for that you have to use multiple inheritance that is considered to be EVIL by many people. It's unfortunate, as it seems we can't efficiently reuse different unrelated (you don't need poop() in order to bark(), and the robot case confirms this assertion) functionality without it.
What is your arguments against my suggestion?
A multiple inheritance implementation is an old-fashioned way of solving these sorts of problems.
Composition is the new way.
You define interfaces which describe a particular behaviour or set of behaviours:
struct Murderer
{
virtual ~Murderer() = default;
void kill();
};
struct Pooper
{
virtual ~Pooper() = default;
void poop();
};
Actual things, like a cat, dog, or robot, inherit (i.e. implement) these interfaces accordingly. You use a dynamic_cast or similar runtime technique to query an object for an interface before making the appropriate action.
I am trying to understand the syntactic difference between composition and inheritance in C++.
I'm hoping someone will provide two simple examples. One example of a class that uses composition and one of a class that uses inheritance.
Sure, why not? Since I like robots, let's make a robot that can walk around and grab things. We'll make one robot using inheritance, and another robot using composition:
class Legs {
public:
void WalkAround() {... code for walking around goes here...}
};
class Arms {
public:
void GrabThings() {... code for grabbing things goes here...}
};
class InheritanceRobot : public Legs, public Arms {
public:
// WalkAround() and GrabThings() methods are implicitly
// defined for this class since it inherited those
// methods from its two superclasses
};
class CompositionRobot {
public:
void WalkAround() {legs.WalkAround();}
void GrabThings() {arms.GrabThings();}
private:
Legs legs;
Arms arms;
};
Note that at least for this example, the CompositionRobot is usually considered to be the better approach, since inheritance implies an is-a relationship, and a robot isn't a particular kind of Arms and a robot isn't a particular kind of Legs (rather a robot has-arms and has-legs).
To expand a little on #jeremy-friesner's answer (and mostly reuse his code), a lot of the time composition is implemented using more classes than that. Essentially the Legs and Arms classes would be implementations of an interface. This makes it easy to inject those dependencies and, hence, mock/stub them out when unit testing the composite object. Then you'd have something like (ignoring virtual destructor...) :
class Walker // interface
{
public:
virtual void Walk() = 0;
}
class Legs : public Walker
{
public:
void Walk() {... code for walking around goes here...}
}
class Grabber // Interface
{
public:
virtual void GrabThings() = 0;
}
class Arms : public Grabber
{
public:
void GrabThings() {... code for grabbing things goes here...}
}
class InheritanceRobot : public Legs, public Arms
{
public:
// Walk() and GrabThings() methods are implicitly
// defined for this class since it inherited those
// methods from its two superclasses
};
class CompositionRobot
{
public:
CompositionRobot(Walker& walker, Grabber& grabber)
: legs(walker),
arms(grabber)
{}
void Walk() {legs.Walk();}
void GrabThings() {arms.GrabThings();}
private:
Walker& legs;
Grabber& arms;
};
So the actual implementation used for legs and arms could be set at run-time instead of compile time.
As an aside, I only wrote this as an answer, rather than a comment on Jeremy's answer, to benefit from the code formatting so, if you feel like up-voting it, please do Jeremy's too.
HTH
UPDATE Sep 14, 2021:
One thing I've noticed in this answer is that I've conflated composition and aggregation. In composition, if the parent object ceases to exist, then so does the child object whereas, in aggregation, the child objects may exist after the parent is destroyed. The description I've given, where references to instances of the child objects are passed in the CompositionRobot constructor implies an aggregation relationship rather than composition. However, if you were to use std::unique_ptr() when defining the parameters and creating the objects, and std::move() when they're stored in the constructor of CompositionRobot, the effect would be much the same as in Jeremy's answer where the objects (rather than a pointer or a reference to them) are defined as class members.
I read so many blogs and I understand how to use virtual function in c++. But, still I don't understand why we use virtual functions. Can you give me a real world example so that I can more easily visualize the actual meaning of virtual function.
An important thing to mention is that inheritance (which the keyword virtual is fundamental for) should not be for the sole purpose of code re-use, use delegation for this.
Delegation would be when we have a class say BroadbandConnection with a method called connection(). Then your manager says we want to add encryption, so you create a class BroadbandConnectionWithEncryption. Your natural instinct may be to use inheritance and then make the new class BroadbandConnectionWithEncryption derive from BroadbandConnection.
Drawback's to this is that the creator of the initial class had not designed it for inheritance so you would need to change its definition to make the method connection() virtual so you can override its behavior in the derived class. This is not always ideal. A better idea is to use delegation here for the purpose of code reuse.
class BroadBandConnection
{
public:
void Connection (string password)
{
//connection code.
}
};
class BroadBandConnectionWithEndcryption
{
public:
void Connection (string password)
{
mbroadbandconnection.Connection(password);
//now do some stuff to zero the memory or
//do some encryption stuff
}
private:
BroadBandConnection mbroadbandconnection;
};
The keyword virtual is used for the purpose of polymorphism. As the name suggest, it is the ability for an object to have more than one form. This sort of decision would be made at the time of designing an interface or class.
class IShape
{
virtual void Draw () = 0;
};
class Square
{
void Draw()
{
//draw square on screen
}
};
class Circle
{
void Draw()
{
//draw circle on screen
}
};
I made Draw() pure virtual with the = 0. I could have left this out and added some default implementation. Pure virtual makes sense for Interfaces where there is no reasonable default implementation.
What this lets me do is pass around a Shape object to various methods and they do not need to be concerned with what I have just given them. All they know is that I have to provide something that supports the ability for a shape to draw itself.
IShape* circle = new Circle ();
IShape* square = new Square ();
void SomeMethod (IShape* someShape)
{
someShape->Draw(); //This will call the correct functionality of draw
}
In the future as people begin thinking of new shapes, they can derive from IShape and so long as they implement some functionality for Draw. They can pass this object to SomeMethod.
First, this.
Now, a real life example. I have a program with a GUI with three tabs. Each tab is an object of a class that derives from a common base, TabBase. It has a virtual function OnActivate(). When a tab is activated, the dispatcher calls it on the current tab. There's some common action and there are actions that are specific to this tab. This is implemented via virtual functions.
The benefit is that the controller does not need to know what kind of tab it is. It stores an array of TabBase pointers, and just calls OnActivate() on them. The magic of virtual functions makes sure the right override is called.
class TabBase
{
virtual void OnActivate()
{
//Do something...
}
};
class SearchTab: public TabBase
{
void OnActivate() //An override
{
TabBase::OnActivate(); //Still need the basic setup
//And then set up the things that are specific to the search tab
}
}
We have one base class (animal) that have method, that can be implemented differently by it's children (say). When we declare this method virtual, we can adress that method and it will be implemented from it's children's definition. You don't have to use virtual if you adress children's overloaded methods, but you have to, when you adress parent's methods.
For example, if you have a vector of animals each one of whom is different. You declare method (say) as virtual and call it from animal class and it will be called from corresponding child.
Correct me if I'm wrong, that's how I understood it.
They actually give an example on Wiki
http://en.wikipedia.org/wiki/Virtual_function
using animals. Animals is the super class, all animals eat (the superclass virtual function). Each animal may eat differently than all the other animals (overriding the virtual function). I have a list of arbitrary animals, and when I call the eat function, they will display their own differing eating habit.
If you are familiar with Java - that should be easy. In Java, ALL class methods are effectively virtual. If you override it in a derived class, and you call it via a base class reference, the override will be called, not the base.
That's not the default behavior in C++. If you want a function to behave in that way, you have to declare it as virtual in the base class. Easy enough.
Java is choke full of virtual functions. It just does not have an explicit keyword for them.
The purpose of virtual functions is to achieve dynamic dispatch.
You say you are familiar with Java, so then for a real world use of virtual functions, think of any place in Java where you would have used an interface or used #Override on a public/protected method.
The decision to use virtual functions is a simple matter. You just need to know when you'd want to override a base method. Take the following code as an example:
class animal
{
public:
void sound()
{
cout << "nothing";
}
};
class bird : public animal
{
public:
void sound()
{
cout << "tweet";
}
};
In this case, I'd want to override bird(). But what if I didn't? This is what would happen:
animal * a = new bird;
a->sound();
**Output**
nothing
The screen would say nothing because for all intents and purposes, C++ only sees an animal. However, if you declared it virtual, it knows to search for the lowest method in the class hierachy. Try it again:
class animal{
public:
virtual void sound(){cout<<"nothing";}
};
class bird : public animal
{
public:
void sound()
{
cout << "tweet";
}
};
animal * a = new bird;
a->sound();
**Output**
tweet.
Hope this helps.
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.