2015-02-05 69 views
0

Heyo,當涉及調用對象作爲它們的父類型時,我對如何重寫方法有點困惑。當使用父類型時覆蓋C++方法

這裏是我的示例代碼:

#include <iostream> 
#include <cstdlib> 
#include <vector> 

using namespace std; 

class A { 
public: 
    A() { 
     std::cout << "Made A.\n"; 
    } 
    void doThing() { 
     std::cout << "A did a thing.\n"; 
    }; 
}; 

class B : public A { 
public: 
    B() { 
     std::cout << "Made B.\n"; 
    } 
    void doThing() { 
     std::cout << "B did a thing.\n"; 
    }; 
}; 

class C : public A { 
public: 
    C() { 
     std::cout << "Made C.\n"; 
    } 
    void doThing() { 
     std::cout << "C did a thing.\n"; 
    }; 
}; 

int main(int argc, char** argv) { 
    std::cout << "\n"; 

    std::cout << "Make objects: \n"; 
    A a; 
    B b; 
    C c; 

    std::cout << "Call objects normally: \n"; 
    a.doThing(); 
    b.doThing(); 
    c.doThing(); 

    std::cout << "Call objects as their parent type from a vector: \n"; 
    vector<A> vect; 
    vect.push_back(a); vect.push_back(b); vect.push_back(c); 

    for(int i=0;i<vect.size();i++) 
     vect.data()[i].doThing(); 

    return 0; 
} 

,這裏是輸出我得到:

Make objects: 
Made A. 
Made A. 
Made B. 
Made A. 
Made C. 
Call objects normally: 
A did a thing. 
B did a thing. 
C did a thing. 
Call objects as their parent type from a vector: 
A did a thing. 
A did a thing. 
A did a thing. 

在另一種語言(如Java)相同的代碼會產生這樣的輸出:

Make objects: 
Made A. 
Made B. 
Made C. 
Call objects normally: 
A did a thing. 
B did a thing. 
C did a thing. 
Call objects as their parent type from a vector: 
A did a thing. 
B did a thing. 
C did a thing. 

總之,如何在C++中實現第二個輸出?

回答

1

每當您將一個Derived對象通過值函數傳遞給函數Base時,會發生稱爲「切片」的操作。基本上,只有Derived對象的Base部分被使用。

您需要通過引用或指針傳遞對象以避免這些問題。例如,聲明

f(Base&) 

允許傳遞一個Derived對象,即允許你寫

f(Derived) 

此外,爲了使運行時多態性,你的函數必須標明virtual。 Java默認情況下隱含標記爲虛擬的一切。但是,這是C++,你不支付你不使用的東西(虛擬函數是開銷)。 PS:在你的代碼中,即使你想要,你也不能使用std::vector的引用。但是,你可以用使用std::reference_wrapper它允許你以「模擬」引用的std::vector的對象:

std::vector<std::reference_wrapper<A>> vect 

,並使用get成員函數來檢索參考

for(int i=0;i<vect.size();i++) 
    vect[i].get().doThing(); 

或者,也許更簡單,只是使用一個std::vector<A*>

+0

Righo!更改爲「矢量 vect;」並推送數據爲「vect.push_back(&b);」結果輸出我想要的。謝謝! – 2015-02-05 17:05:10

1

您需要使用virtual關鍵字才能使函數在子類中被覆蓋。

+0

這就是它的一部分。你也不能用值獲得多態性,所以向量將不得不存儲指針。 – 2015-02-05 16:58:34

+0

我將我的代碼更改爲virutal void doThing(),但它仍輸出相同的內容。有沒有使用虛擬的特定方式? – 2015-02-05 16:59:39

+1

@newObjekt:看到我的評論。雖然Java總是在處理對象類型時使用引用,但C++可以使用對象值。但是,如果將派生類的值分配給基類值,則會丟失派生的對象信息。這被稱爲[切片](http://stackoverflow.com/q/274626/10077)。 – 2015-02-05 17:01:08

1

好吧所以發生了什麼事: 使物體: 我覺得很明顯。在一個對象被構造和它的默認構造函數打印

Made A 

當你實例化B對象首先它的父類被完全構造。因此,在這種情況下,父類是它就會用默認的構造函數,將B類剩餘部分被建成後可打印出

Made A 

並運行它的構造函數,打印出

Made B 

構建實例化時發生同樣的事情C

在對象上調用函數: 它只是一個簡單的函數調用,因爲你覆蓋每個類中的函數而不是父函數。

當您創建對象的向量時,您將對象複製到它們中,因爲您不通過引用或指針。您還沒有寫入複製構造函數,因此默認的逐位複製將運行。這種方式從一個B類對象中獲得一個A類對象,該類對象的功能將打印出A做過的事情,而不是B做過的事情。同樣的情況發生在C.