2016-03-21 25 views
0

我有幾個類d與以下形式的公共部分:如何與一個協變參數來覆蓋的函數(在一個抽象基類)

class D 
{ 
    public: 
     D& foo(); 
     void bar(D&); 
} 

我想建立一個單一的抽象類從該他們都從中得到。

我的(幼稚)的嘗試是:

// in .h file 
class B 
{ 
    public: 
     virtual B& foo() = 0; 
     virtual void bar(B&) = 0; 
} 

class D : public B 
{ 
    public: 
     D& foo() override; 
     void bar(D&) override; 
} 

// in .cpp file 
D& D::bar() {return *(new D());} 
void D::foo(D& d) {} 

這未能編譯(我最終意識到是)一個相當合理的理由:任何功能覆蓋

void bar(B&)=0; 

必須定義函數用於任何參考類型B的參數。所提供的候選

virtual void bar(D&) override; 

僅針對參考類型D的參數(較小的集合)定義。

請注意,這對函數foo不是問題。事實上,如果你用條註釋掉這三行,所有的東西都編譯得很好。

我認爲這種現象的技術解釋是C++不支持參數的協方差(但它確實支持參數中的協變性)。

答案後C++ covariance in parameters表明,我不能爲我的類定義的接口(即抽象類)D.

有一些簡單的或常規的方法來創建所有單「界面」我的班D?或者,也許隱藏這些類的不同實現有不同的設計模式。

在此先感謝您的意見和建議。

+2

沒有什麼像'協變參數'。返回類型可能是協變的。 –

+0

必須在派生類中使用在基類中聲明的***精確***簽名來實現純虛擬成員函數。 –

+1

@πάνταῥεῖ允許協變返回類型。 – NathanOliver

回答

0

你不能,而且有很好的理由。派生類不能添加比它所衍生的接口更嚴格的先決條件,而不會破壞存在的所有OOP原則。通過要求參數在您的界面實現中更具體,這正是您正在做的。

的論點可以作出這樣的事情可能是有用的:

struct IfaceA {}; 
struct IfaceB : IfaceA {}; 

struct Base { void f(IfaceB &); }; 
struct Derived : Base { void f(IfaceA &); }; 

這減少的前提條件,而不是增加他們,所以沒關係。這只是在C++或其他任何我知道的語言中完成的,所以你不能這樣做。

在這兩種情況下,您都可以使用替代參數類型使過載並調用覆蓋的版本。

這與返回類型相反。返回值是後置條件。您可以使發佈條件更加具體,但不能使其更加廣泛。所以你可以返回你的派生類型,但不能通過返回更抽象的類型來擴展它。儘管至少有一個非常常用的編譯器非常糟糕,但C++實現了協變返回,所以存在大量的錯誤。

0

根據您提供的代碼嘗試覆蓋兩個不同的函數簽名。 您擁有的最佳選項是使用相同的簽名,然後投射結果。 一個簡單的例子,

// in .h file 
class B 
{ 
    public: 
     virtual B* foo() = 0; 
     virtual void bar(B*) = 0; 
}; 

class D : public B 
{ 
    public: 
     B* foo() override; 
     void bar(B*) override; 
}; 

// in .cpp file 
B* D::foo() 
{ 
    D* p=new D(); 
    return p; 
} 
void D::bar(B* d) 
{ 
    D* casted=dynamic_cast<D*>(d); 
} 


int main(void) 
{ 
    D son; 
    B* father=dynamic_cast<B*>(son.foo()); 
} 

我希望這能解決你的問題,或者至少給你一個線索。

+0

這似乎很有用,但我總是對使用dynamic_casts的智慧有點懷疑。我想有些情況下演員陣容是合適的,但我通常會盡量避免他們。你確定這個解決方案是否符合最近發佈的C++指南? – sitiposit

+0

我也反對dynamic_cast,檢查虛擬表的成本太高,我知道你在做什麼,你應該總是使用static_cast來代替。關於C++的指導方針,我不得不承認,我已經研究了c/C++的老式方法,但是,我可以給我任何建議/來源,我會很感激。 –

0

你可以使用模板化的基類嗎?

template <typename Deriving> 
struct Base { 
    virtual Deriving& foo() = 0; 
    virtual void bar(Deriving&) = 0; 
}; 

struct Deriving : public Base<Deriving> { 
    ... 
}; 

您沒有單個接口類,但有一個接口模板,它有時候通用性不夠。

+0

這看起來就像我在找的東西。我提出的問題看起來很自然。這個解決方案是解決問題的標準/常用方法嗎?我正在努力保持良好的編碼習慣並避免「黑客行爲」。 – sitiposit

+0

這被稱爲「奇怪的循環模板模式」。這是一個相當常見的模式,不應該引起任何關注。 – joelw