2012-02-10 70 views
7

在下面的例子中,使用隱式接口(情況2和3;模板)與使用顯式接口(情況1;指向抽象類的指針)有什麼優點/缺點?隱式與顯式接口

代碼不改變:

class CoolClass 
{ 
public: 
    virtual void doSomethingCool() = 0; 
    virtual void worthless() = 0; 
}; 

class CoolA : public CoolClass 
{ 
public: 
    virtual void doSomethingCool() 
    { /* Do cool stuff that an A would do */ } 

    virtual void worthless() 
    { /* Worthless, but must be implemented */ } 
}; 

class CoolB : public CoolClass 
{ 
public: 
    virtual void doSomethingCool() 
    { /* Do cool stuff that a B would do */ } 

    virtual void worthless() 
    { /* Worthless, but must be implemented */ } 
}; 

例1:一個非模板化類,它的基類的指針,其提供顯式的接口:

class CoolClassUser 
{ 
public: 
    void useCoolClass(CoolClass * coolClass) 
    { coolClass.doSomethingCool(); } 
}; 

int main() 
{ 
    CoolClass * c1 = new CoolA; 
    CoolClass * c2 = new CoolB; 

    CoolClassUser user; 
    user.useCoolClass(c1); 
    user.useCoolClass(c2); 

    return 0; 
} 

情況2:甲模板類提供了一個隱式接口:

template <typename T> 
class CoolClassUser 
{ 
public: 
    void useCoolClass(T * coolClass) 
    { coolClass->doSomethingCool(); } 
}; 

int main() 
{ 
    CoolClass * c1 = new CoolA; 
    CoolClass * c2 = new CoolB; 

    CoolClassUser<CoolClass> user; 
    user.useCoolClass(c1); 
    user.useCoolClass(c2); 

    return 0; 
} 

情況3:一個模板類,其templ吃類型提供一個隱式接口(此時,從不導出CoolClass

class RandomClass 
{ 
public: 
    void doSomethingCool() 
    { /* Do cool stuff that a RandomClass would do */ } 

    // I don't have to implement worthless()! Na na na na na! 
}; 

template <typename T> 
class CoolClassUser 
{ 
public: 
    void useCoolClass(T * coolClass) 
    { coolClass->doSomethingCool(); } 
}; 

int main() 
{ 
    RandomClass * c1 = new RandomClass; 
    RandomClass * c2 = new RandomClass; 

    CoolClassUser<RandomClass> user; 
    user.useCoolClass(c1); 
    user.useCoolClass(c2); 

    return 0; 
} 

案例1要求對象在被傳遞給 useCoolClass()是CoolClass的子(和實施毫無價值( ))。另一方面,案例2和3將採取任何類具有 doSomethingCool()函數。

如果代碼的用戶總是細子類 CoolClass,那麼情況1具有直觀意義,因爲 CoolClassUser將總是期待一個 CoolClass的實現。但是,假設此代碼將成爲API框架的一部分,所以我無法預測用戶是否想要子類 CoolClass或者滾動具有 doSomethingCool()函數的自己的類。

一些相關的帖子:

https://stackoverflow.com/a/7264550/635125

https://stackoverflow.com/a/7264689/635125

https://stackoverflow.com/a/8009872/635125

+0

您的案例1和案例2不會編譯。指針初始化的方式是錯誤的。 – Novelocrat 2012-02-10 19:39:35

+0

@Novelocrat固定。 – 2012-02-10 21:18:57

回答

3

是來到我的腦海,爲什麼你會喜歡案例1一些注意事項:

  • 如果CoolClass不是純粹的接口,即實現的一部分也是繼承的(儘管你也可以爲例2/3提供它),例如,以基類的形式);
  • 如果有理由使CoolClassUser在一個二進制而不是一個頭中實現(這不僅是保護,而且還可以是代碼大小,資源控制,集中式錯誤處理等);如果你想存儲指針並在以後使用它,那麼情況1似乎更好:(a)將它們全部放在同一個容器中更容易,並且(b)你需要將實際的數據類型存儲爲對於案例2/3,想到的解決方案是在模板包裝的幫助下將其轉換爲「顯式」界面(即案例1)。

原因病例的2/3可能是preferrable:

  • 如果您稍後決定worthless()現在是值得的東西,並開始使用它,在案例2,你會得到編譯時上課錯誤它沒有被執行。在案例1中,沒有任何東西會提醒您實現這些功能,除非運行時錯誤(除非您是幸運的)。
  • Case2/3的性能可能略好,但代價是代碼的大小。

在某些情況下,它可能純粹是個人喜好的問題,無論是您的用戶還是您的用戶。

1

請記住,在情況#2,#3,你根據模板參數,這意味着在通話時編碼器必須正確實例正確類型的模板參數。根據函數的使用方式,這可能會產生一些問題,您希望爲用戶創建一個抽象接口,而不必擔心被傳遞的對象的類型......即「句柄」或某些指向派生對象的其他指針,該對象使用多態性將對象從一個API函數傳遞到另一個函數。例如:

class abstract_base_class; 

abtract_base_class* get_handle(); 
void do_something_with_handle(abstract_base_class* handle); 
void do_something_else_with_handle(abstract_base_class* handle); 
//... more API functions 

現在,你的API框架可以將一個對象返回給你的代碼的用戶,他們不需要知道對象是什麼......他們只需要知道它描述某種類型的界面,您當然可以公開地在某處顯示標題。但他們不需要知道任何有關你傳回給他們的東西的「膽量」。你可以給它們一個指向你控制實現的派生類型的指針。您只需要爲API中最通用的函數類型提供模板。否則,不得不實例化僅用於採取abstract_base_class*功能的模板,只是爲用戶輸入更多樣板代碼。