C++, Object model and polymorphism: runtime checking
C++, Object model and polymorphism: runtime checking
Let us have the followig class
class Animal { public: virtual short getFeet() = 0; virtual void setFeet(short feet) = 0; };
and 2 derived classes:
class Bird : public Animal { short flying_speed; public: virtual short getFeet() { return 2; } // always 2 feet virtual void setFeet(short feet_) { }; virtual short getFlyingSpeed() { return flying_speed; } }; class Mammal : public Animal { short feet; // whale: 0 feet, human: 2 feet, others: 4 feet public: virtual short getFeet() { return feets; } virtual void setFeet(short feet_) { feet = feet_ }; };
A) A question related to the object model:
Let us focus on property feets. What to do with the atribute feet and methods getFeet() and setFeet().
Some animals belonging to the same category have different amount of feet, so they use an attribute feet and methods getFeet()
and setFeet()
are common.
Some animals have the same amount of feet, so they do not use an own attribute feet
. The method getFeet()
returns a constant, the method setFeet()
does not do anything.
Is this model correct or any changes related to the variable feet are recommended (there are a lot of animals without feets)?
B) A question related to the polymorphism
One category has some specific feature; for example birds, which are flying. So it makes sense to ask at what speed they are flying, see the method getFlyingSpeed()
.
We would like to use a polymorphism and create a pointer pointing to a Bird
:
int main(int argc, _TCHAR* argv[]) { Animal *a = new Bird(); std:: cout << a->getFlyingSpeed() << '\t'; // error! }
I supposed, that the compiler checks a correct assignment not during a compilation, but there is only a runtime check of both types. But I was wrong because...
Error C2039:
getFlyingSpeed
: is not a member ofAnimal
Is there any way, how to use an attribute, which is not common to all classes, together with a polymorphism? Or can only runtime type chcek will be forced?
Maybe this model is not correct. How to redesing it?
Thanks for your help.
Answer by tune2fs for C++, Object model and polymorphism: runtime checking
Your call animal should only provide features which are common to all animals (or the animals you wnat to use in you application). For other features not common to all animals can use multiple inheritance to implement the desired behaviour for sub types of animals. So a subtype might be FlyingAnimals.
class FlyingAnimal { public: virtual short getFlyingSpeed() = 0; virtual ~FlyingAnimal(){} }; class Bird : public Animal, public FlyingAnimal { short flying_speed; public: virtual short getFeet() { return 2; } // always 2 feet virtual void setFeet(short feet_) { }; virtual short getFlyingSpeed() { return flying_speed; } };
You can define also a subclasses for animals with feets and so on.
Or instead of multiple inheritance you can build it like that:
class FlyingAnimal: public Animal { public: virtual short getFlyingSpeed() = 0; virtual ~FlyingAnimal(){} }; class Bird : public FlyingAnimal { short flying_speed; public: virtual short getFeet() { return 2; } // always 2 feet virtual void setFeet(short feet_) { }; virtual short getFlyingSpeed() { return flying_speed; } };
In your main method however you need to define what type of animal you suspect.
int main(int argc, _TCHAR* argv[]) { FlyingAnimal *a = new Bird(); std:: cout << a->getFlyingSpeed() << '\t'; // not an error anymore! }
Answer by Gir for C++, Object model and polymorphism: runtime checking
You could check which type it is at runtime with dynamic_cast
:
void tryFlying(Animal *a) { Bird *b = dynamic_cast(a); if (b) { std::cout << "uninitialized speed junk " << b->getFlyingSpeed() << std::endl; } else { std::cout << "can't fly" << std::endl; } } int main() { Animal *a, *b; a = new Bird; b = new Mammal; tryFlying(a); tryFlying(b); return 0; }
Output:
uninitialized speed junk -27757 can't fly
Comes with a free memory leak (needs virtual destructors).
Answer by Pete Becker for C++, Object model and polymorphism: runtime checking
Animal *a = new Bird();
The assignment is correct; Bird
is derived from Animal
, so a Bird*
can be stored in an Animal*
. But the type of the pointer a
is pointer to Animal
, not pointer to Bird
, so you can only call member functions that are defined for Animal
. That's why the compiler objects to
a->getFlyingSpeed()
getFlyingSpeed
is not a member function of Animal
. On the other hand, the call
a->getFeet()
is okay, because getFeet
is a member function of Animal
. It will call the version of getFeet
that's defined in Bird
, because Bird
is the type of the object that a
points to.
In short, C++ is statically typed; that is, types are determined at compile time, which is why you can only call Animal
member functions on a
.
Answer by Roman Saveljev for C++, Object model and polymorphism: runtime checking
A)
Public
setFeet()
looks weird (well, lets say this is amateur surgeon sandbox), but I would recommend redesigning object hierarchy as follows: class Animal { public: virtual short getFeet() const = 0; } class SurgeonDelight : public Animal { short feetNumber; public: short getFeet() const {return feetNumber;} virtual void setFeet(short feet) {feetNumber = feet;} }
Then you inherit all animals with constant number of feet from Animal
, all others from SurgeonDelight
. We make setFeet
virtual for its descendants to be able implementing some side effects of changing number of feet. SurgeonDelight
hides actual data member to enforce "single reason to change" thing.
B)
You can employ RTTI and dynamic_cast
, but basically you should construct Bird
to be a Bird
Somebody told me "your design is bad, if you have to do dynamic_cast". I tend to agree. I coded for over 5 years and did not use dynamic_cast
for a single time (because we did not support it). If there are more animals in your zoo with flying speed (like Turtle
) you should inject additional layers of generalization into your hierarchy.
Another not so nice approach I saw in few libraries is to leave "extension" API:
virtual void* extension(int functionId, int param) = 0;
Yep, quite ugly, but gets you there
Answer by E_net4 for C++, Object model and polymorphism: runtime checking
A) I suppose all animals cannot change the number of feet once they are created, unless you really want to allow this. Therefore, it is best to define feet
in the constructors of Animal and removing the associated setter. Subclasses of Animal can then call the base constructor without a problem. I wouldn't find dedicating a class for animals with feet worthwhile, since it usually makes sense to indicate an animal has no feet at all.
B) What has been suggested is the most appropriate: create an interface class (with purely virtual methods) for flying animals. Calling it FlyingAnimal
, you can use a FlyingAnimal*
pointer to call your method. If you want to know if an animal can fly in runtime, a bool canFly()
method can be used, thus making a dynamic cast safe.
Answer by stakx for C++, Object model and polymorphism: runtime checking
There are a few things wrong here.
First, it doesn't make sense that the property feet
can even be changed during the lifetime of an Animal
instance. Even if humans have 2 feet, they cannot suddenly grow a third or a 77th one. Also, it probably should be the same across all instances of the same animal type. So feet
ought to be fixed per class, not be settable per instance.
Try this:
class Animal { private: short _feet; protected: Animal(short feet) : _feet(feet) { } public: short getFeet() { return _feet; } // and call it 'getNumberOfFeet`; see below. // note: no longer virtual! }; class Whale : public Animal { public: Whale() : Animal(0) { } }; class Mammal : public Animal { ? }; class Ape : public Mammal { public: Ape() : Animal(2) { } };
(Sorry if there are a few errors. My C++ skills are a little rusty these days.)
Note also that I set the number of feet for all apes to 4. This is because if you derived a class Human
from Ape
(let's assume that we all believe in the theory of evolution...), and Ape
had 4 feet, would it make sense to contradict this by saying that humans only had 2? Something would be wrong in your type hierarchy, because Human
s obviously wouldn't be proper Ape
s.
Btw., it would make more sense if your property were called numberOfFeet
, not feet
. With feet
, you'd expect to get back a std::vector
or something similar. After all, when someone says to you, "Give me your feet", you would put your legs in their lap or something, but you wouldn't say, "Two!"... would you?
Second, to your code example:
int main(int argc, _TCHAR* argv[]) { Animal *a = new Bird(); std:: cout << a->getFlyingSpeed() << '\t'; // error! }
Two possibilities:
You always know that
a
refers to aBird
. Then why do you declare variablea
asAnimal*
instead of asBird*
?a
may or may not refer to aBird
. Then why do you calla->getFlyingSpeed()
? What should happen ifa
is not aBird
instance?
One possible solution:
class Animal { public: void explainYourself() = 0; }; class Bird : public Animal { ? public: void explainYourself() { std::cout << "I am flying at " << getFlyingSpeed() << " mph." << std::endl; }
};
class Mammal : public Animal { ? public: void explainYourself() { std::cout << "Hello, I have " << getFeet() << " feet." << std::endl; } }; int main(?) { Animal *a = new Bird(); a->explainYourself(); return 0; }
Answer by Thomas L Holaday for C++, Object model and polymorphism: runtime checking
This is a rich topic. Consider this brutal solution:
struct FlyingSpeed { bool meaningful; short speed; static const FlyingSpeed nofly; }; const FlyingSpeed FlyingSpeed::nofly = { false, 0 };
If the flying_speed method returned an object of type FlyingSpeed instead of a short. each instance of Animal could respond to the getFlyingSpeed method by returning a FlyingSpeed object. Birds, bats, bees would have a meaningful FlyingSpeed. Worms, wolves, walruses would have a non-meaningful FlyingSpeed. Kangaroos could have a flying speed or not depending on your view of hopping. Spiders would have a meaningful FlyingSpeed while balooning, and a non-meaningful FlyingSpeed once they build their webs.
The class object nofly is a convenience so that terrestrial, arborial, and aquatic animals can have terse constructors.
Fatal error: Call to a member function getElementsByTagName() on a non-object in D:\XAMPP INSTALLASTION\xampp\htdocs\endunpratama9i\www-stackoverflow-info-proses.php on line 72
0 comments:
Post a Comment