2012-09-21 22 views
2

例如使用私有繼承來隱藏實現是個好主意嗎?

// Implementation. 
struct PrivatePoint { 
    void SomePrivateMethod(); 

    double x; 
    double y; 
} 

struct Point : private PrivatePoint { 
    double DistanceTo(const Point& other) const; 
} 

這似乎與Pimpl idiom相似。這有兩個我很喜歡的優點:

  1. SomePrivateMethod是可測試的。如果SomePrivateMethod在Point中被聲明爲private,那麼您將無法從測試中調用它。如果您將其聲明爲公開或在Point中受保護,則測試可以調用它,但Point的常規用戶也是如此。
  2. 與Pimpl成語中的相比,訪問私人數據更容易閱讀和書寫,因爲您不必經過指針,

Point::DistanceTo(const Point& other) { 
    SomePrivateMethod(); 

    double dx = other.x - x; 
    double dy = other.y - y; 
    return sqrt(dx * dx + dy * dy); 
} 

Point::DistanceTo(const Point& other) { 
    ptr->SomePrivateMethod(); 

    double dx = other.ptr->x - ptr->x; 
    double dy = other.ptr->y - ptr->y; 
    return sqrt(dx * dx + dy * dy); 
} 

回答

2

你的建議也有一些缺點....

用戶可能對夫婦自己的「私人」類。

pimpl習語的主要目的是作爲編譯防火牆,允許在實現文件中指定私有成員,因此可以在不觸及頭部的情況下進行更改,並且必須/觸發客戶端重新編譯,這與僅重新鏈接截然不同速度更快,甚至可能甚至不需要涉及客戶端應用程序的任何操作(如果更新是針對動態加載的庫)。你失去了這些好處。

SomePrivateMethod是可測試的。如果SomePrivateMethod在Point中被聲明爲private,那麼您將無法從測試中調用它。如果您將其聲明爲公開或在Point中受保護,則測試可以調用它,但Point的常規用戶也是如此。

還有其他方便的選擇,例如:你可以用測試代碼聲明友誼,或者使用預處理器構建暴露數據的測試模式。

+0

重新耦合:我不認爲這會在實踐中出現問題,因爲PrivatePoint被明確標記爲「不要使用」。 – allyourcode

+0

重新安裝防火牆:有沒有辦法可以輕鬆讀寫Pimpl?快速構建是好的,但是可讀的代碼也是如此:/ – allyourcode

+0

@allyourcode:你對pimpl習語的讀寫有什麼擔心? –

0

沒有,因爲反正你不得不發佈聲明的基類的頭文件。私有繼承保護您或您的用戶免於不必要的數據/方法訪問。

+0

用戶將如何訪問我不希望他們訪問的數據?我的庫永遠不會實例化PrivatePoint;它只存在於Point的基類中。如果用戶實例化私人點,那麼將來的圖書館版本將打破他。但那是他自己的錯,因爲這是一個非常明顯的錯誤(imho)。即使他忽略了所有說明永遠不會說明這個CLASS的大寫的評論,這個名字本身應該是非常明顯的。 – allyourcode

+1

@allyourcode:假設人們不會使用在界面中提供的類型,這會假設很多。歷史已經證明,用戶傾向於做別的事情,要求微軟和那些在操作系統的無法訪問的結構中利用未記錄的字段的用戶,並迫使微軟爲那些不僅意味着不被使用,而且甚至無法通過接口提供的操作系統 –

+0

@allyourcode然後告訴我你的情況和這一個之間有什麼區別: struct Point { private: void SomePrivateMethod(); double x; double y; public: double DistanceTo(const Point&other); } – Serge

1

私有繼承是非常相似的成分(和PIMPL),但也有一些缺點考慮:

  1. 你需要讓PrivatePoint定義在一個公共頭部可見。這引入了對PrivatePoint的編譯時間依賴性,並且需要在每次更改PrivatePoint時重新編譯Point的客戶端。使用pimpl時,情況並非如此,只需轉發declare PrivatePoint即可: struct PrivatePoint;

  2. 封裝是星期。通過私有繼承,擴展Point的客戶端可以實現他們自己的SomePrivateMethod版本(C++允許覆蓋私有虛擬方法)。這是不是在你的例子有問題,但是,如果SomePrivateMethod被宣佈虛擬

如果使用平普爾,你也可以很容易地進行單元測試PrivatePoint。

+0

感謝您提及私有虛擬成員函數可以被覆蓋。這似乎是錯誤的,但是再一次,我們正在談論C++:P。 – allyourcode