2012-03-06 36 views
3

在java中,我們可以定義不同的接口,然後我們可以爲具體的類實現多接口。在C++中設計ABC(抽象基類)的好習慣

// Simulate Java Interface in C++ 
/* 
interface IOne { 
    void MethodOne(int i); 
    .... more functions 
} 

interface ITwo { 
    double MethodTwo(); 
    ... more functions 
} 

class ABC implements IOne, ITwo { 
    // implement MethodOne and MethodTwo 
} 
*/ 

在C++中,一般來講,我們應該避免多重繼承的使用,雖然多繼承確實有它在某些情況下邊緣。

class ABC { 
public: 
    virtual void MethodOne(int /*i*/) = 0 {} 
    virtual double MethodTwo() = 0 {} 

    virtual ~ABC() = 0 {} 

protected: 
    ABC() {} // ONLY ABC or subclass can access it 
}; 

問題1>基於對ABC的設計,我應該改善任何其他的東西,以使它成爲一個體面的ABC?

Question2>好的ABC不應該包含成員變量,而應該將變量保存在子類中嗎?

問題3>正如我在評論中指出的那樣,如果ABC必須包含太多純函數?有沒有更好的辦法?

+0

不確定您的實際代碼是否與第一個示例代碼塊相似,但接口類中的方法聲明需要'virtual'關鍵字;只有在最初聲明爲「虛擬」後纔是可選的。 – ssube 2012-03-06 17:29:44

+6

這是否編譯?在純虛函數中0之後的大括號是錯誤 – 2012-03-06 17:30:02

+1

@AlessandroPezzato:這是正確的;純虛擬成員函數不能在類的主體中定義。它可以在類的定義之外定義。但是,某些編譯器(例如Visual C++,至少在Visual C++ 2010 SP1中)將會按原樣接受代碼。 – 2012-03-06 17:34:20

回答

9
  1. 除非有必要,否則不要爲純虛擬方法提供實現。
  2. 不要讓你的析構函數純粹是虛擬的。
  3. 不要讓你的構造函數受到保護。你不能創建一個抽象類的實例。
  4. 更好地隱藏源文件中的構造函數和析構函數的實現,不污染其他對象文件。
  5. 使您的界面不可複製。

如果這是一個接口,最好不要在那裏有任何變量。否則,它將是一個抽象基類,而不是一個接口。

太多的純函數是可以的,除非你可以用較少的純函數來完成它。

+0

Re 3:構造函數是虛構的,但是......如果這個類是抽象的,並且沒有數據成員,爲什麼要寫一個構造函數呢?編譯器提供了默認的工作。 – 2012-03-06 17:42:59

+0

@JamesKanze:你的意思是說有一個構造函數,而不是「虛擬構造函數」,對吧?但是,有一個構造函數沒有錯,只是更多的輸入。 – 2012-03-06 17:44:44

+0

我的意思是保護,而不是虛擬。使構造函數受到保護或提供明確的構造函數句號沒有任何問題。正如你所說,只是額外的打字。而且有人可能會認爲它不符合界面的一般模式,所以是誤導性的(但我​​不確定一般模式是如此普遍認可以至於有所作爲)。 – 2012-03-06 18:00:36

9

在C++中,一般來講,我們應該避免多重繼承

的像任何其他語言功能的使用,你應該使用多重繼承無論它是適當的。接口通常被認爲是多重繼承的適當使用(參見例如COM)。

ABC的構造函數不需要保護 - 它不能直接構造,因爲它是抽象的。

不應將ABC析構函數聲明爲純虛擬(當然,它應聲明爲虛擬)。如果不需要派生類,則不應要求派生類實現用戶聲明的構造函數。

接口不應該有任何狀態,因此不應該有任何成員變量,因爲接口只定義瞭如何使用某些東西,而不是如何實現。

ABC應該不會有太多成員函數;它應該具有所需的數量。如果存在太多,那麼顯然應該刪除那些未使用或不需要的,或者將接口重構爲幾個更具體的接口。

+0

請注意,如果您接受多重繼承(並且我從未見過廣泛使用它的非平凡應用程序),則其他所有問題都變得無關緊要。 – 2012-03-06 17:45:16

+0

@ q0987這取決於。最安全的策略可能總是從界面實際上繼承,但實際上,這往往是矯枉過正的;例如,實現通常不是爲了支持繼承而設計的。另一方面,當接口本身繼承接口時,虛擬繼承通常是一個好主意;在我的例子中,我可能應該這樣做:儘管沒有基礎的多個實例,但是一旦實現從moer繼承而不是一個複合接口就很容易發生。 – 2012-03-06 18:55:02

4

基於美國廣播公司的設計,我應該改進任何其他的東西,以使它成爲一個體面的ABC?

你有幾個語法錯誤。出於某種原因,您不允許在類定義中放置純虛函數的定義;無論如何,你幾乎肯定不想在ABC中定義它們。因此,聲明通常會是:

virtual void MethodOne(int /*i*/) = 0; // ";" not "{}" - just a declaration 

有沒有真正在做的析構函數純粹的任何一點,儘管它應該是虛擬的(或在某些情況下,非虛擬和保護的 - 但它是最安全的,使之虛)。

virtual ~ABC() {} // no "= 0" 

不需要受保護的構造函數 - 它是抽象的事實已經阻止了實例化,除非作爲基類。

確實,好的ABC不應該包含成員變量,而應該將變量保存在子類中?

通常,是的。這給了界面和實現之間的清晰分離。

正如我在評論中指出的那樣,如果ABC必須包含太多純函數呢?有沒有更好的辦法?

該接口應該儘可能地複雜,因爲它不需要。如果有些是不必要的,則只有「太多」的功能;在這種情況下,擺脫他們。如果界面看起來太複雜,那麼可能會嘗試做更多的事情;在這種情況下,你應該能夠將它分解成更小的接口,每個接口都有一個目的。

3

第一:爲什麼我們應該避免C++中的多重繼承?我從未見過 這是一個沒有廣泛使用的大型應用程序。從多個接口繼承 就是使用它的一個很好的例子。

需要注意的是Java的interface是隻要你想使用 編程合同打破—,你堅持使用抽象類, 他們不允許多重繼承。在C++中,但是,它很簡單:

class One : boost::noncopyable 
{ 
    virtual void doFunctionOne(int i) = 0; 
public: 
    virtual ~One() {} 
    void functionOne(int i) 
    { 
     // assert pre-conditions... 
     doFunctionOne(i); 
     // assert post-conditions... 
    } 
}; 

class Two : boost::noncopyable 
{ 
    virtual double doFunctionTwo() = 0; 
public: 
    virtual ~Two() {} 
    double functionTwo() 
    { 
     // assert pre-conditions... 
     double results = doFunctionTwo(); 
     // assert post-conditions... 
     return results; 
    } 
}; 

class ImplementsOneAndTwo : public One, public Two 
{ 
    virtual void doFunctionOne(int i); 
    virtual double doFunctionTwo(); 
public: 
}; 

或者,你可以有一個複合接口:

class OneAndTwo : public One, public Two 
{ 
}; 

class ImplementsOneAndTwo : public OneAndTwo 
{ 
    virtual void doFunctionOne(int i); 
    virtual double doFunctionTwo(); 
public: 
}; 

,並從它繼承,這曾經是很有道理的。

這是或多或少的標準成語;在不可能將 作爲界面中的任何前置條件或後置條件(通常爲 調用反轉)的情況下,虛擬功能可以是公共的,但是一般而言,它們將是私有的,以便您可以執行預定義和 後置條件。

最後,請注意,在很多情況下(特別是如果類 表示一個值),您將直接實施它,而不使用接口 。與Java不同,你不需要一個單獨的接口來維護 實現在與012xx定義—類別不同的​​文件中,這就是C++默認工作的方式(類中的類定義爲 ,但源中的實現代碼文件)。

+0

「doFunctionOne」和「doFunctionTwo」應分別在「One」和「Two」的類中保護還是公開而不是私有? – q0987 2012-03-06 19:02:46

+0

@ q0987當然不是'public'。我更喜歡'私人',就像其他一些專家一樣,但也有贊成'保護'的論點。 – 2012-03-06 19:46:02

+0

當你定義一個私有虛擬函數時,哪個子類可以覆蓋它? – q0987 2012-03-12 14:56:45