2010-05-28 51 views
3

好的,我正在写一个游戏,它有一个向导,里面充满了孩子们的类(goomba,koopa,boss1),我需要这样做的时候我打电话更新它调用childclasses各自的更新。我设法创造了一个我的问题的例子。添加虚拟功能的问题

#include <stdio.h> 
class A{ 
    public: 
     virtual void print(){printf("Hello from A");} 
}; 

class B : public A{ 
    public: 
     void print(){printf("Hello from B");} 
}; 


int main(){ 
    A ab = B(); 
    ab.print(); 
    while(true){} 
} 

输出想要的:“你好,从B” 输出了:“你好,从A”

我如何得到它调用B的打印功能?

+2

是由切片的问题? – 2010-05-28 18:10:14

+2

是的,切片。请参阅http://stackoverflow.com/questions/274626/what-is-the-slicing-problem-in-c – jpalecek 2010-05-28 18:10:50

+0

顺便说一句,这不应该被标记为'C',因为'C'语言没有继承的设施。删除了'C'标签。 – 2010-05-28 18:19:08

回答

14

多态性仅适用于指针和引用。如果您将B指定给A,则其将变为A,并且您将丢失所有B特定信息,包括方法覆盖。这被称为“切片”;将B零件分配给父类的对象时,它们会从对象“切片”。

在另一方面,如果你分配一个B*A*,它看起来A*,但仍然是真正的指向B,所以B特定信息仍然存在,并B的虚拟覆盖将会被使用。

尝试:

int main(){ 
    A* ab = new B(); 
    ab->print(); 
    delete ab; 
    while(true){} 
} 

这同样也适用于分配BA&(参考TO- A),例如

int main(){ 
    B b; 
    A& ab = b; 
    ab.print(); 
    while(true){} 
} 
+0

这工作。谢谢。 – William 2010-05-28 18:15:24

+0

+1:哇,不知道。 – Simon 2010-05-28 18:15:58

+4

你们两位(来自你的发布历史)似乎都来自Java背景。重要的是要记住,在Java中,所有对象都是真正的引用(在Java中,这实际上介于C++指针和C++引用之间),并且所有对象都是虚拟的,所以多态性总是有效的。在C++中,对象是值类型,因此分配两个对象确实会将右侧的值指定给左侧,而不是将左侧指定为与右侧相同的对象。这意味着切片可能发生,因为'A'对象无处存储'B'的额外信息。 – 2010-05-28 18:19:23

3

您的虚拟关键字放置正确,但您需要使用指针或引用。

0

需要由子类的任何处理之前调用父类的更新方法:

struct Base_Class 
{ 
    virtual void update(void) 
    { 
    cout << "Updating Base_Class.\n"; 
    } 
}; 

struct Goomba : public Base_Class 
{ 
    void update(void) 
    { 
    // Invoke the parent method first. 
    Base_Class::update(); 

    // Perform Descendant operations 
    cout << "Updating Goomba\n"; 
    } 
}; 

下面是执行:

#include <iostream> 
using std::cout; 

void Update_Player(Base_Class& b) 
{ 
    b.update(); 
    return; 
} 

int main(void) 
{ 
    Goomba g; 
    g.update(); 

    Goomba g2; 
    std::vector<Base_Class *> container; 
    container.push_back(&g); 
    container.push_back(&g2); 

    std::vector<Goomba>::iterator iter; 
    for (iter = container.begin(); 
     iter != container.end(); 
     ++iter) 
    { 
     Update_Player(*(*iter)); 
    } 

    return 0; 
} 
+0

如果你想这样做,最好让Update *不是虚拟的,而是让基类Update成为该名称的唯一函数,然后在适当的地方调用虚拟DoUpdate函数。然后更容易,因为然后子类的实现者不需要记住何时调用基类更新函数。 – 2010-05-28 23:04:16