2016-04-28 41 views
9

的比方說,我們有以下層次:如何防止調用基實現的方法

class Abstract 
{ 
public: 
    virtual void foo() = 0; 
}; 

class Base : public Abstract 
{ 
public: 
    virtual void foo() override; //provides base implementation 
}; 

class Derived : public Base 
{ 
public: 
    virtual void foo() override; //provides derived implementation 
}; 

如果Base::foo()曾經被稱爲Derived對象,對象將不同步和數據將被破壞的。它繼承了Base的數據結構及其操作,但需要執行其他操作,因此只調用Base::foo()將省略這些額外的操作,結果Derived的狀態將被破壞。

因此,我想,以防止Base實施foo所以這直接調用:

​​

理想情況下,應該給我的某些種類的編譯時錯誤。或者什麼都不做或以其他方式被阻止

但是這可能是我違反規則的多態性和應該使用成分代替但這需要一個額外的大量打字...

+0

在'Base'中,'protected:'而不是'public:'方法有問題嗎?此外,這似乎也是一個體面的候選人,可以在'Base'中重新聲明爲純虛擬的,*並提供一個'Base'實現,這並不常見,但確實發生了。如果「派生」應該總是被執行,那麼它似乎是一個不錯的選擇。 – WhozCraig

+0

@WhozCraig我不知道我可以提供純虛擬方法的實現。如果它被那些繼承它的實現重新實現,那麼這會阻止實現永遠被調用嗎? – Resurrection

+1

@Resurrection no,這就是爲什麼'Base :: foo'上的'protected'會進來。但是它*做的*是強制派生類仍然提供覆蓋,同時還提供了一個通用的Base實現,它們可以在不拋棄的情況下調用一個額外的成員功能進入不適。 – WhozCraig

回答

22

如何template method pattern

class Abstract 
{ 
public: 
    void foo() { foo_impl(); } 
private: 
    virtual void foo_impl() = 0; 
}; 

class Base : public Abstract 
{ 
private: 
    virtual void foo_impl() override; //provides base implementation 
}; 

class Derived : public Base 
{ 
private: 
    virtual void foo_impl() override; //provides derived implementation 
}; 

然後

void test(Abstract& obj) { 
    obj.foo(); // the correct foo_impl() will be invoked 
} 
Derived d; 
test(d); // impossible to call the foo_impl() of Base 
+8

一般來說,隱藏多態性隱藏在非多態模板函數後面是一個好主意,不僅在此案件。 – leemes

8

你可以讓所有的foo()方法非public,然後有一個非 - virtual函數在Abstract類中簡單地稱爲foo

+1

對於「can」可能會讀「可能應該」(作爲一般規則 - 即使您不想阻止人們在腳下自己拍攝)。 –

+1

@Martin Bonner - 真實,有效。鏈接[非虛擬接口](https://en.wikipedia.org/wiki/Non-virtual_interface_pattern)等... –

12

您可以探索template method pattern。它可以更好地控制所涉及方法的執行。

class Abstract 
{ 
public: 
    virtual void foo() = 0; 
}; 

class Base : public Abstract 
{ 
protected: 
    virtual void foo_impl() = 0; 
public: 
    //provides base implementation and calls foo_impl() 
    virtual void foo() final override { /*...*/ foo_impl(); } 
}; 

class Derived : public Base 
{ 
protected: 
    virtual void foo_impl() override; //provides derived implementation 
}; 

的圖案出現在輸入輸出流庫sync()pubsync()方法。

爲防止直接調用並保持一致狀態,您需要在堆棧中的正確位置獲得foo方法的final實現。如果意圖禁止層次結構頂部的直接呼叫,則可以移動_impl方法。

另請參閱非虛擬接口the NVI pattern


請記住,以及該重載方法不必具有相同的訪問說明符的Abstract類。您也可以在派生類privateprotected中創建方法;

class Abstract 
{ 
public: 
    virtual void foo() = 0; 
}; 

class Base : public Abstract 
{ 
    virtual void foo() override; //provides base implementation 
}; 

class Derived : public Base 
{ 
    virtual void foo() override; //provides derived implementation 
}; 

注:除非另有打算,改變了訪問說明符可以認爲是不好的設計 - 所以基本上如果你更改訪問符,應該應該是一個很好的理由這樣做。

+0

感謝您的提示!我以前使用過這種模式,但由於某種原因,它沒有發生在我身上,它會解決我目前的問題。無論如何,我認爲虛擬foo在這種情況下是一種矯枉過正的情況,而具有虛擬foo_impl的Abstract中的非虛擬foo實際上正如其他人所建議的那樣。但是,是的,這是爲了當我想讓真正的完全控制。 – Resurrection

+0

當然,您在模板方法模式中掛鉤的級別取決於您想要做什麼。自從你在提問中提到這一點以來,我把它放在那裏。僅使用純虛函數維護類也可能是有利的(它可以將鏈接器依賴關係最小化,例如通過dll邊界)。 – Niall

+0

有趣的一點。重寫時我會考慮它。關於編輯:這當然是可能的(雖然我在其他地方讀過,但是重新標識這樣的標識符是一個糟糕的設計),但是在這種情況下,因爲Base必須按照Abstract所設想的工作,所以我不能隱藏公共接口或其中的一部分案件。 – Resurrection

相關問題