Search code examples
c++polymorphismvirtual

Virtual function call from inside a base class function


I have two classes, Object and Ball. Ball is derived from Object. Object has a virtual function "move" and a non virtual function "moveFast" that calls move. Class Ball redefines the move function from it's parent class.

#include <iostream>

struct Object
{
    virtual void move(int dist)
    {
        std::cout<<"Moving "<<dist<<std::endl;
    }
    void moveFast(int multiplier) 
    {
        move(10*multiplier);
    }
};

struct Ball : public Object
{
    void move(int dist)
    {
        std::cout<<"Rolling "<<dist<<std::endl;
    }
};

class List
{
    struct Node
    {
        Node* next;
        Object ele;
        Node(Object e, Node* n=NULL) : ele(e), next(n){}
    };

    Node* head;
public:
    List() : head(NULL){}
    void addObj(Object o)
    {
        if(head==NULL)
        {
            head =  new Node(o);
            return;
        }

        Node* current = head;
        while(current->next!=NULL)
        {
            current=current->next;
        }
        Node* obj = new Node(o);
        current->next=obj;
    }
    void doStuff()
    {
        Node* current = head;
        while(current!= NULL)
        {
            current->ele.moveFast(10);
            current=current->next;
        }
    }
};

int main()
{
    Object a,b,c;
    Ball d;
    List list;

    list.addObj(a);
    list.addObj(b);
    list.addObj(c);
    list.addObj(d);
    list.doStuff();
}

The List class takes in Objects and calls their moveFast function. Because a,b, and c are just Objects I would expect the first 3 lines of output to be "Moving 100". d however, is an instance of the Ball class. So I would expect the 4th line of output to say "Rolling 100", because Ball redefined the move function.

Right now all the output prints

Moving 100
Moving 100
Moving 100
Moving 100

Is there a way to get Ball's definition of move called from List?


Solution

  • The problem is that you store your Objects in the list by value. virtual functions will only work on pointers. the moment you try to add an object to the list through list::void addObj(Object o). The argument is passed by value. This means that it is copied and if you copy a base class only the base class functionality will be copied it's called the slicing problem (like dyp mentioned). you should change your nodes to hold a pointer to the object and redo your add object function to take a pointer to the element to prevent copying and slicing.

    like this

     class List
    {
        struct Node
        {
            Node* next;
            Object* ele;
            Node(Object* e, Node* n=nullptr) : ele(e), next(n){}
        };
    
        Node* head;
    public:
        List() : head(nullptr){}
        void addObj(Object* o)
        {
            if(head==nullptr)
            {
                head =  new Node(o);
                return;
            }
    
            Node* current = head;
            while(current->next!=nullptr)
            {
                current=current->next;
            }
            Node* obj = new Node(o);
            current->next=obj;
        }
        void doStuff()
        {
            Node* current = head;
            while(current!= nullptr)
            {
                current->ele->moveFast(10);
                current=current->next;
            }
        }
    };
    
    int main()
    {
        Object a,b,c;
        Ball d;
        List list;
    
        list.addObj(&a);
        list.addObj(&b);
        list.addObj(&c);
        list.addObj(&d);
        list.doStuff();
        return 0;
    }
    

    which outputs:

    Moving 100
    Moving 100
    Moving 100
    Rolling 100