2010-05-28 113 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