2014-01-21 46 views
1

我得到了意想不到的結果,至今我都無法解釋自己。C++協方差意外行爲

雖然生成了兩次同一類BASIC2(繼承自BASIC1)的對象,但在生成的對象上使用好友operator<<時會生成不同的消息。下面

查看代碼,我讓D(一類DerivedBase繼承的對象)產生BASIC2類的一個對象,並調用它的朋友operator<<。這產生了預期的消息"BASIC2 object"

我讓B生成類BASIC1的一個對象,並將其稱爲其朋友operator<<。如我所料,這輸出"BASIC1 object"

然後我用虛擬繼承讓B2Base* B2 = &D;)生成一個對象BASIC2。我遵循調試器(Visual Studio 2010)中的代碼流,這會正確生成BASIC2的對象。但是,朋友operator<<未在BASIC2對象上調用,但使用類BASIC1的朋友operator<<(因此輸出"BASIC1 object")。

另請注意,我希望BASIC2繼承自BASIC1,因爲我想利用協變性行爲。

int main(int argc, char* argv[]) { 
    Base B; 
    Derived D; 
    Base* B2 = &D; 
    std::cout << *D.generate(0) << std::endl; 
    std::cout << *B.generate(0) << std::endl; 
    std::cout << *(B2->generate(0)) << std::endl; 
    system("pause"); 
} 

輸出是:

 
BASIC2 object 
BASIC1 object 
BASIC1 object 
class BASIC1 { 
public: 
    friend std::ostream& operator<<(std::ostream& os, const BASIC1& basic) { 
    os << "BASIC1 object"; 
    return os; 
    } 
}; 

class BASIC2 : public BASIC1 { 
    friend std::ostream& operator<<(std::ostream& os, const BASIC2& basic) { 
    os << "BASIC2 object"; 
    return os; 
    } 
}; 

class Base { 
public: 
    virtual BASIC1* generate(double num) const { 
    return new BASIC1(); 
    } 
protected: 
private: 
}; 

class Derived : public Base { 
public: 
    virtual BASIC2* generate(double num) const override { 
    return new BASIC2(); 
    } 
protected: 
private: 
}; 
+0

那麼'B2'的靜態類型是'Base',所以編譯器看到的函數是'BASIC1 * Base :: generate(double)'(動態調度調用'Derived :: generate')。不過,返回類型是'BASIC1 *'。並且基於表達式*(B2-> generate(0))'的靜態類型選擇'operator <<'。 – dyp

+1

@dyp:I.e.這個'operator <<'不是''virtual'「。 –

+1

運行時/動態分派只在成員函數中完成。 – molbdnilo

回答

5

操作< <的選擇是基於靜態類型在編譯時知道您的對象。爲了實現你想要不定義兩個運營商< <功能是什麼,但一個只在BASIC1:

friend std::ostream& operator<<(std::ostream& os, const BASIC1& basic) { 
     Write(os); 
     return os; 
    } 

,並在這兩個BASIC1定義虛擬函數編寫和BASIC2做你想做什麼:

virtual void Write(std::ostream& os) const { 
    os << "BASIC1 object"; 
} 
+0

應該使用:virtual void Write(std :: ostream&os)const {「BASIC1 object」; },因爲我傳遞一個const Basic&object – user3116431

+0

是的,最好是儘可能強制執行const。 –

+0

寫入必須被賦予一個const方法限定符,否則(它不會被編譯),你必須保證你傳遞給operator <<的const對象不會被修改。 – user3116431

1

謝謝!對我來說,我的問題的最終結論是:如果您通過動態調度生成對象的對象,請給朋友操作員打電話<(std :: ostream & os,const Base &對象)。您需要在運營商< <中使用虛擬功能,以允許動態調度運營商< <。