2013-08-24 34 views
3

我有一個簡單的代碼,它不能與引用(多態)正確地工作。C++中的多態性無法正確地與引用一起工作

#include <iostream> 
#include <string> 

class Base { 
public: 
    Base() {} 
    virtual ~Base() {} 
    virtual std::string text() const { 
     return "Base"; 
    } 
}; 

class Derived: public Base { 
public: 
    Derived(Base& _b): b(_b) {} 
    virtual ~Derived() {} 
    virtual std::string text() const { 
     return b.text() + " - Derived"; 
    } 

private: 
    Base& b; 
}; 

int main(int argc, char const *argv[]) 
{ 
    Base b; 
    Derived d1(b); 
    std::cout << d1.text() << std::endl; 

    Derived d2(d1); 
    std::cout << d2.text() << std::endl; 
    return 0; 
} 

輸出:

Base - Derived 
Base - Derived 

在輸出的第二行我預計:Base - Derived - Derived。我閱讀了一些資源和多態性可以很好地與參考和指針一起工作,但在這種情況下,它並沒有。如果我用指針替換引用,它會再次工作。那麼,任何人都可以給我一些解釋?

非常感謝!

+2

看起來你正在調用'Derived'的默認copy-ctor,因爲你從未提供過它。在第二個例子中,默認值比第一個更好(明確的是'Base'類型)。爲了證明這一點,在'Derived(Base&)'中設置一個斷點或輸出一個調試信息。你應該看到它在第二個例子中不被觸發。換句話說,d1只是d2的一個副本。 – WhozCraig

回答

7

你調用默認的拷貝構造函數來Derived。因此,當完成d2將是一個簡單的成員副本d1,並且他們的b成員將引用相同的Base實例。

爲了證明這一點,添加到您的Derived

class Derived: public Base { 
public: 
    Derived(Derived& d) : b(d) {} 
    Derived(Base& _b): b(_b) {} 
    virtual ~Derived() {} 
    virtual std::string text() const { 
     return b.text() + " - Derived"; 
    } 

private: 
    Base& b; 
}; 

有了這個你的輸出就會變成:

Base - Derived 
Base - Derived - Derived 

而且只要注意,這不是一個偉大的思想或恆星學習的榜樣的多態性。 (但這是一個有趣的例子,建設覆蓋)。還要注意,這不是默認複製構造的典型覆蓋(其中參數是const-ref-type)。因此這部分原因並不是最大的樣本。

0

正如在評論中正確指出的那樣,它現在使用默認的拷貝構造函數,這就是你的觀察結果與兩個輸出相同的原因。所以,d1只是複製到d2中,而不是用於d2中的基本成員變量。

1

你的d1d2都有類型Derived所以這是正常工作。通常參考是顛倒的;例如

Base b; 
Derived d; 
Base &dr = d; 

std::cout << b.text() << std::endl; 
std::cout << dr.text() << std::endl; 

這裏text()通過Base類型調用而後者將在Derived調用版本。

請注意,它通常不允許通過基類來初始化派生類。假設您添加了類型爲Derived2的能力或狀態與Derived完全不同。此構造函數將允許

Derived2 d2; 
Derived d1(d2); 

這可能是一個非常糟糕的主意。

+1

我相信他知道這一點,他想知道爲什麼目前的方式不起作用,他提出。 – lpapp

1

如果您測試代碼,您將看到當您撥打Derived d2(d1) Derived :: Derived(Base &) 構造函數未被調用。這是因爲d1參數與 隱式拷貝構造函數更好地匹配,它將b成員從d1複製到d2。

爲了看到您期望的行爲,您可以明確地將d1轉換爲(Base&)d1。如果你這樣做 所以你會得到像下面這樣(用儀器)代碼:

#include <iostream> 
#include <string> 

class Base { 
public: 
    Base() {} 
    virtual ~Base() {} 
    virtual std::string text() const { 
     return "Base"; 
    } 
}; 

class Derived: public Base { 
public: 
    Derived(Base& _b): b(_b) {std::cout << "init'ed with: " << _b.text() << std::endl;} 
    virtual ~Derived() {} 
    virtual std::string text() const { 
     return b.text() + " - Derived"; 
    } 

private: 
    Base& b; 
}; 

int main(int argc, char const *argv[]) 
{ 

    std::cout << "Creating Base" << std::endl; 
    Base b; 

    std::cout << "Creating d1" << std::endl; 
    Derived d1(b); 
    std::cout << d1.text() << std::endl; 

    std::cout << "Creating d2" << std::endl; 
    Derived d2(d1); 
    std::cout << d2.text() << std::endl; 

    std::cout << "Creating d3" << std::endl; 
    Derived d3((Base&)d1); 
    std::cout << d3.text() << std::endl; 

    return 0; 
} 

由此給預期輸出:

Creating Base 
Creating d1 
init'ed with: Base 
Base - Derived 
Creating d2 
Base - Derived 
Creating d3 
init'ed with: Base - Derived 
Base - Derived - Derived 
+0

呃。另一個沒有D(B&)構造函數的原因! – sfjac

相關問題