2011-12-29 62 views
1

我正在研究爲C++ STL容器向量構建包裝器(將有更多的功能,然後向量可以提供(加載,保存等),所以它需要是一個包裝) 。構建STL向量的接口

我的包裝的消費者需要迭代通過元素,如果我公開了STL迭代器,稍後更改實現將需要我的調用者重新編譯(加上我覺得我打破封裝)。

我想創建一個只返回原始類型的接口,以確保客戶端不需要重新編譯,如果我改變實現。我正在考慮將矢量大小公開爲一個整數(類似於MFC CArray的工作方式),並且還重載了[]運算符,調用者可以用這種方式循環矢量。

我的問題:

  1. 如果我想返回一個int的矢量大小請問是怎麼工作,SIZE_TYPE? Size_type看起來不像應該在接口中公開,因爲如果它改變了,調用者將需要重新編譯。如果size_type可能大於整數(我不希望我會擁有那麼多元素!),我會很樂意施加某種限制。
  2. 使用[]運算符循環矢量顯著差那麼使用迭代器

編輯:刪除單詞「通用」 - 這已無關的模板,它只是一個類。還澄清「公開原始類型」是指返回一個int而不是數據成員本身的方法。

+2

使用迭代器和'size_type'比使用整數索引和'operator []'更具有通用性。迭代器抽象了容器的類型,'size_type'允許size值是任何整型。 – 2011-12-29 06:04:48

+0

如果稍後更改實施以使用Boost等類型,那麼使用我的STL迭代器的客戶端將需要重新編譯它們嗎? – TownCube 2011-12-29 06:08:54

+1

你可以爲迭代器編寫自己的包裝器,就像使用vector一樣。但是,是的,如果'size_type'的類型發生了變化,那麼它們將不得不重新編譯。 – 2011-12-29 06:16:56

回答

2

您可以定義一個singelton IFC(即您的客戶將引用純虛)類。我認爲這種設計模式被稱爲「Singelton工廠方法」。 我希望我的長答案能幫助你:)。

只要你不改變公共接口(方法列表),如果你改變你的代碼,你的客戶就不需要重新編譯。

類似:

myClassIfc.h:

Class myClassIfc 
{ 
public: 
    virtual ~myClassIfc();  

    ///// list all your pure virtual public ifc methods here //// 
    void m_zRunMyMethod(int nNumber) = 0; 
    int m_nSize() = 0; 

    static myClassIfc* ms_pGetImplObj();  

protected:   
    myClassIfc(); 
    static myClassIfc* ms_pImplObj; 
} 

inline myClassIfc* myClassIfc::ms_pGetImplObj() 
{ 
    return ms_pImplObj; 
} 

myClassIfc.cpp:

#include myClassIfc.h 

myClassIfc::myClassIfc() 
{ 
} 
myClassIfc::~myClassIfc() 
{ 
} 

myClass.h - 實現你的純虛類

Class myClass: public myClassIfc 
{ 
public: 
    virtual ~myClass();  

    void m_zRunMyMethod(int nNumber); 
    int m_nSize(); 

    static void ms_zCreate(); 
    static void ms_zDestroy(); 

protected:   
    myClass(); 

private: 
    vector<int> myInternalVector; 
} 

MyClass的.cpp:

#include myClass.h 

void myClass::m_zRunMyMethod(int nNumber) 
{ 
    /// your action 
    printf("%d\n", nNumber); 
} 

int myClass::m_nSize() 
{ 
    return int(myInternalVector.size()); 
} 

void myClass::ms_zCreate() 
{ 
    if (NULL != ms_pImplObj) 
    { 
     return; 
    } 
    ms_pImplObj = (myClass*) new myClass(); 
} 

void myClass::ms_zDestroy() 
{ 
    if (NULL == ms_pImplObj) 
    { 
     return; 
    } 
    delete ms_pImplObj; 
    ms_pImplObj = NULL; 
} 

現在上述長期基礎性工作後,你的客戶端需要使用

#include myClassIfc.h 


void main(void) 
{ 
    myClassIfc::ms_pGetImplObj()->m_zRunMyMethod(5); 
    myClassIfc::ms_pGetImplObj()->m_nSize();   
} 

我沒有上面列出的唯一的事情就是你的內存管理,這意味着誰創造的單對象本身(調用派生類的ms_zCreate()靜態API)。您可以從其他地方或直接從您的代碼中調用它。

通知您可以操縱上述IFC方法是非singelton實施。 只要ifc類不更改,如果您修改派生(實現)類,則客戶端代碼將不需要重新編譯。

+0

看起來像一個偉大的工作。 – 2011-12-29 09:13:16

+0

更好的方法是使用pimpl並轉發,而不是使用繼承來試圖隱藏實現細節。 – 2011-12-29 14:56:19

1

我不建議實際使用索引和運算符[]。不要直接暴露向量迭代器,而要在您的類中創建迭代器類型定義,它最初與向量迭代器相同,但您可以將其更改爲任何想要的內容,而無需客戶端更改代碼。然後你可以使用正常的開始/結束/查找方法。

+0

我知道他們不需要更改代碼,但是如果typedef更改,他們是否需要重新編譯? – TownCube 2011-12-29 06:32:03

+0

是的。可能避免重新編譯的唯一方法是創建自己相應的迭代器包裝,並將整個實現隱藏在源文件中。請記住,ABI破壞(並因此強制重新編譯)在C++中很難避免。 – 2011-12-29 14:45:58

1

有很多你在問現代C++設計中的蒼蠅。我會把你推薦給赫伯斯特特和安德烈亞歷山德斯庫的書C++ Coding Standards。查看課程設計中的這些章節標題:

Class Design and Inheritance 
32. Be clear what kind of class you’re writing. 56 
33. Prefer minimal classes to monolithic classes. 57 
34. Prefer composition to inheritance. 58 
35. Avoid inheriting from classes that were not designed to be base classes. 60 
38. Practice safe overriding. 66 
39. Consider making virtual functions nonpublic, and public functions nonvirtual. 68 
41. Make data members private, except in behaviorless aggregates (C-style structs). 72 
42. Don’t give away your internals. 74 
44. Prefer writing nonmember nonfriend functions. 79 

要特別注意#44。而不是重寫STL向量,創建操作STL迭代器的非成員函數。這也與#35相關。 STL類不是真的被設計爲基類。 #33是我對你的評論感到畏縮的原因,你想添加「加載」和「保存」功能到STL向量。這些功能聽起來像是非會員功能。

哦,你真的以爲這通過:

我想建立一個只暴露原始類型,以確保客戶將不需要重新編譯,如果我改變實現的接口。我正在考慮將矢量大小公開爲整數

一方面,您希望使用合成將矢量隱藏到另一個類中。好的。不是最靈活的設計,但可能需要與非C++代碼進行交互。但是,您想要將類的數據成員公開爲接口的一部分。這可能會造成客戶需要重新編譯的問題。這沒有意義。要麼你想要全封裝,要麼不關心頻繁的重新編譯。這是什麼? (並且請不要在MFC CArray類中建模任何東西。這是可怕的。)

+0

感謝您的回答。我不必公開大小,這只是我認爲可以讓客戶使用[]運算符來遍歷數組的迭代方式之一。我正在尋找一種方法,它將返回一個表示數組大小的整數(而不是暴露數據成員),因爲從查看各種API並且它們的入口點重新調整int似乎是安全和常見的。 – TownCube 2011-12-29 10:29:28

+0

+1用於非成員,非好友算法。 – 2011-12-29 14:57:43

+0

@Cube,STL Vector已經有了這樣一個方法:size()。它是一個返回size_type的const方法,通常定義爲size_t,它通常是一個無符號整數。 – jmucchiello 2011-12-29 21:54:04

0

你說「我想建立一個只露出 原始類型,以確保客戶將不需要重新編譯的接口。」只是 相反是真的;如果要避免重新編譯,則需要使用編譯防火牆慣用語將所有內容全部設置爲用戶定義的類型。 在迭代的情況下,這可能有一個不可接受的性能 處罰:迭代成語需要深拷貝,而事實上, 沒什麼可內聯很可能影響到優化爲好。

我想避免重新編譯完全可能是不合理的,因爲 的C++的方式工作。更重要的是,如果您更改實現,則您希望避免在客戶端代碼中修改 。例如,在這個 的情況下,您要確切地定義您想要保證的內容;如果你決定迭代器進入你的類必須是隨機的 訪問迭代器,那麼到std::vector<>::iterator的typedef是 可能就足夠了;如果你想要保證的只是前向迭代 (爲了讓實現更加自由),你想要 考慮將std::vector<>::iterator包裝在你自己的迭代器中, 只會公開你願意保證 支持的操作所有未來的版本。

請注意,如果您稍後決定使用不支持隨機訪問迭代器的實現,則不能支持[]也不支持[]。 與僅支持前向迭代器的 相比,支持[]對未來的實現施加了更多約束。