I just started learning polymorphism and am stuck with smth. I have a base class Object
, and a derived class Ball
. I would like to achieve polymorphism and use Ball
methods on an Object
pointer as such:
Object *ball = new Ball(P, dPdt, s, Forces);
cout << ball->getP() << " position\n" << ball->getdPdt() << " speed\n";
The first one, getP()
, works fine because it's a method of the Object
class and Ball
's constructor calls Object
's constructor to initialize it. Now, when it comes to getdPdt()
, the compiles complains that no member named getdPdt
in Object
, which is obvious since I defined it in Ball
. What am i doing/understanding wrong here ?
PS: I need by *ball
to be an Object
since this is for a physics simulation and everything must be an Object
in order to simulate it. Later on, I guess I'll add a vector<unique_ptr<Object>>
to keep track of
CodePudding user response:
If Object
is a polymorphic type, a quick and dirty solution is to downcast.
Object *ball = new Ball(P, dPdt, s, Forces);
dynamic_cast<Ball*>(ball)->getdPdt();
This works only if you're sure that ball
points to an Ball
object. Otherwise, checks must be done before calling getdPdt
on the casted pointer.
Note that the above solution goes against the goal of polymorphism that you wanted to achieve. Polymorphism is about providing a single interface (in this case the Object
class) to entities of different types (in this case all of the subclasses of Object
, including Ball
). The interface should be designed such that all required operations are accessible through Object*
.
Better polymorphic solution if defining getdPdt
for Object
makes sense:
class Object {
public:
virtual int getdPdt() const {
return 0;
}
};
class Ball : public Object {
public:
int getdPdt() const override {
return 42;
}
};
int main() {
Object* obj = new Ball();
// this returns 42, despite obj being `Object*`
obj->getdPdt();
}
CodePudding user response:
C is a statically typed language. If the static type of an expression is Object
, then it can do whatever an Object
can do (and nothing else). If it's a Ball
, then it can do whatever a Ball
can do. You want it to be an Object
but be able to do what Ball
can do. You cannot have both, you need to decide one way or another.
Polymorphism is not about making properties that are exclusively defined for Ball
magically available through an Object
pointer. It is about Ball
properties inherited from Object
behave in a Ball
-specific way.
Note that
Object *ball = new Ball(P, dPdt, s, Forces);
dynamic_cast<Ball*>(ball)->getdPdt();
can only be read this way:
Object *ball = new Ball(P, dPdt, s, Forces);
// I want to create a Ball but forget that it's a Ball
// I want to only remember that it's an Object
dynamic_cast<Ball*>(ball)->getdPdt();
// No! It's really a Ball after all!
// My decision to forget it was wrong! I regret it!
Now anyone looking at these two lines of code would wonder whether you really know what you want, and rightfully so. Don't do things that you regret at the next line.
The situation is more complicated if these two lines are not next to each other. For example, you may have a vector of Object*
:
std::vector<Object*> objects;
objects.push_back(new Ball(whatever));
// in a totally different function in a totally different file
dynamic_cast<Ball*>(objects[i])->getdPdt();
Now the questions that the astute reader would be asking are different, but no less difficult. Why is it known that objects[i]
points to a Ball
? Why isn't this knowledge encoded in the type of the variable? That's what types are for in C . Recall that C is a statically typed language. Any exception to this (and dynamic_cast
is naturally an exception, what with it having the word "dynamic" in it) should be extremely well justified. If there is some code that decides what to do based on the type of the variable, that code should generally be a method in the base class.
In the end there are only two different things you can do while staying in the object-oriented design paradigm. Either add getdPdt
to Object
, so that it is available for all Object
s; or put your Ball
s in a separate basket whose type says that there are Ball
s inside, and not just Object
s. dynamic_cast
is a deviation from the OO paradigm. Whether a deviation is acceptable to you is up to you to decide, but if you find yourself deviating from it a lot, then perhaps you want to consider a different paradigm.