从基类函数内部调用虚函数

时间:2014-10-11 22:50:10

标签: c++ polymorphism virtual

我有两个类,Object和Ball。 Ball来自Object。 Object有一个虚拟函数“move”和一个调用move的非虚函数“moveFast”。 Class Ball从它的父类重新定义了move函数。

#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();
}

List类接受Objects并调用它们的moveFast函数。因为a,b和c只是对象,我希望前3行输出为“Moving 100”。 但是,是Ball类的一个实例。所以我希望第四行输出说“滚动100”,因为Ball重新定义了移动功能。

现在所有输出都打印

Moving 100
Moving 100
Moving 100
Moving 100

有没有办法让Ball从List中调用移动的定义?

2 个答案:

答案 0 :(得分:0)

问题是您按对象将对象存储在列表中。虚函数只适用于指针。您尝试通过list :: void addObj(Object o)将对象添加到列表的那一刻。参数按值传递。这意味着它被复制,如果你复制一个基类,只会复制基类功能,它被称为切片问题(如提到的dyp)。您应该更改节点以保存指向对象的指针并重做添加对象函数以获取指向元素的指针以防止复制和切片。

像这样

 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;
}

输出:

Moving 100
Moving 100
Moving 100
Rolling 100

答案 1 :(得分:0)

许多人都说过,List中有一个切片问题。

List::Node存储了一个实际的Object,所以当Ball的一个实例传递到addObj(Object o)时,Ball的附加功能被“切片”,只有它的基类“Object”存储的部分残留。

更改Node类以存储Object指针而不是Object实例可修复此问题。此更改还需要更改addObj函数以接收指针。 List类现在看起来像这样:

class List
{
    struct Node
    {
        Node* next;
        Object* ele;//<-- This is the Big change
        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()以提供更改的输入会产生预期的输出。

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

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