Search code examples
c++polymorphism

Pointer not inheriting base class methods


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


Solution

  • 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 Objects; or put your Balls in a separate basket whose type says that there are Balls inside, and not just Objects. 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.