2010-01-19 104 views
9
class X 
{ 
protected: 
    void protectedFunction() { cout << "I am protected" ; } 
}; 

class Y : public X 
{ 
public: 
    using X::protectedFunction; 
}; 

int main() 
{ 
    Y y1; 
    y1.protectedFunction(); 
} 

這樣我就可以暴露基類的其中一個功能。這兩個類是否違反封裝?

  1. ,這是否違反了封裝的原則?
  2. 是否有一個特定的原因,爲什麼這是標準?
  3. 這有什麼用處,還是會在新標準中改變?
  4. 標準中是否有與此相關的公開問題?
+0

澄清 - 我的問題的目的不是談論破解封裝的暴力或棘手的方法。 我只想討論可能導致此功能的C++的設計決策或一些其他功能。 它只是解決方案的功能隱藏問題的一個副作用,或者它是一個獨立的功能,因爲某些特定的原因在C++中引入 – 2010-01-19 18:43:28

+0

我認爲人們列出所有這些破壞封裝的方式的原因並不是文檔如何這樣做,但要解釋你必須選擇打破封裝。 由於您嘗試訪問私人方法或會員時出現錯誤,這可能會作爲溫和提醒,如果您遵守設計師的意願,可以引導您設計合作解決方案。封裝可以被強制破壞總是給定的。受保護的封裝不能在沒有強制力的情況下被破解,我認爲這不是部分語言的失敗。 – 2010-01-19 19:11:44

回答

12

是它,這就是爲什麼保護已收到的批評公平的份額。

Bjarne的Stroustrup的,C++的創造者,在他的優秀著作對此表示遺憾的設計和C演化++:

我的一個關於保護的擔憂是 正是它使得它太容易 使用常見的基地之一可能 拖泥帶水使用了全局數據....在 回想起來,我認爲保護的方法是 其中「很好的理由」和 時尚克服了我的判斷 和我的經驗法則接受 新的情況下,特徵。

+5

他還表示,他認爲保護職能是一個好主意。這裏的問題是使用「使用」(我認爲這很糟糕),而不是使用「受保護的」。 – 2010-01-19 16:08:50

+0

否。受保護的問題是它沒有真正提供任何保護(這是Bjoarne的論點)。要訪問該方法,所有你需要做的就是繼承這個類,從而打破封裝。在受保護的情況下,不會比使用公開更安全。 – 2010-01-19 17:17:19

+2

@馬丁,該書說保護功能:良好,受保護的數據:不好。你真的讀過它嗎? – 2010-01-19 17:35:58

18

你自己做到了。
你可以寫

class Y : public X 
{ 
public: 
    void doA() 
    { 
     protectedFunction(); 
    } 
}; 

int main() 
{ 
    Y y1; 
    y1.doA(); 
} 

我看不出有任何理由要擔心。
受保護函數是繼承樹中的可重用邏輯塊。如果存在某種內部邏輯或限制,或者如果您確信這不會傷害任何人,則可以將其隱藏起來。

11

我認爲這是斯特勞斯本人認爲封裝和數據完整性的功能內置到C++的目的是保持誠實的人誠實,不停止犯罪。

+4

我喜歡這些類型的問題在C++ FAQ中的一些答案。 '我怎樣才能禁止他人......' - 寫一條評論說'我怎樣才能阻止他們做......' - 寫一條評論說如果他們這樣做,他們會被解僱。 「但是如果我真的,真的想阻止......' - 解僱這個人。 – 2010-01-19 17:15:36

3

No. protectedFunction()受保護。您可以自由地從派生類中調用它,所以您可以直接從Y的公共成員調用它,並從main()中調用此公共成員。

如果你可以用private方法做到這一點,就會出現問題...... (編輯得有點清楚)。

+0

-1:有問題的代碼不會從派生類的上下文中調用受保護的方法。受保護的方法在main()的範圍內調用。如果您刪除'使用'聲明符,代碼不再編譯。 – 2010-01-19 15:54:50

+0

@John,這很明顯!這只是一個捷徑,而不是自己做「蹦牀」功能。它不會增加也不會降低函數的保護級別。 – Hexagon 2010-01-19 16:02:02

+1

@John Dibling:這意味着程序員刻意暴露了這個功能。對這個公共包裝函數進行拍打幾乎是一樣的容易。我看不出有什麼理由在這裏倒下。 – 2010-01-19 16:04:27

1

類設計應該知道,聲明一個成員函數或變量(儘管所有的變量應該是私有的,真的)爲受保護的主要是與聲明它公開(如你表明它很容易獲得訪問受保護的東西)。考慮到這一點,當在基類中實現這些功能以考慮「意外」使用時應該小心。這不是對封裝的違反,因爲您可以訪問它並公開它。

保護只是一種更公開的公開方式!

+0

我想說,更像是一種可以公開宣佈的方式。 – 2010-01-19 16:16:33

2

從語言角度,是不是比在派生類對象創建委託方法,更違反了封裝的:

class Y : public X 
{ 
public: 
    void protectedFunction() { 
     X::protectedFunction(); 
    } 
}; 

一旦一個方法是受保護的,它是提供給派生類按照他們的意願去做。聲明using的意圖不是改變繼承的方法訪問,而是避免方法隱藏問題(如果定義了不同的過載,中的公共方法將隱藏在Y類型中)。

現在,由於語言規則,它可以用來改變'已使用'方法的訪問級別,這是事實,但這並不能真正打開系統中的任何漏洞。最後,派生類不能做任何比他們以前能做的更多的事情。

如果實際的問題是從設計的角度(這裏的設計是應用程序設計,而不是語言設計),特定用法是否違反封裝,那麼很可能是。與公開內部數據或方法的方式完全相同。如果它的目的是通過的X初步設計進行保護,那麼很有可能是Y實現者不希望它是公衆......(或提供爲同一目的的委託方法)

+1

是的,很好的解釋,完全同意 – 2011-10-20 07:04:07

2

使用公共的方法,你應該有Y一個實例,這將不起作用:

int main() 
{ 
    X y1; 
    y1.protectedFunction(); 
} 

如果爲Y在其新定義暴露出一個新的接口它了Y. X仍然受到保護。

2

在C++中,即使專用修飾符也不能保證封裝。沒有人能阻止你在腳下射擊自己。

香草薩特在他的文章「Uses and Abuses of Access Rights」中展示了不同的方式,你可以如何「打破封裝」。

這裏有一個有趣的例子來自Herb的文章。

邪惡魔宏觀

#define protected public // illegal 
#include "X.h" 
//... 
X y1; 
y1.protectedFunction(); 
+1

這是未定義的行爲,部分原因是它打破了一個定義規則。即使重新定義「受保護」是合法的,你也不能指望班級成員處於同一順序。 – 2010-01-19 18:51:44

+1

是的,我知道未定義的行爲。我想表明這是欺騙,即「打破封裝」,但包裝保護功能不是。 – 2010-01-19 20:17:50

1

不,我真的不明白這個問題。

相反的using,你可以這樣做:

class X 
{ 
protected: 
    void protectedFunction() { cout << "i am protected" ; } 
}; 

class Y : public X 
{ 
public: 
    void protectedFunction() { X::protectedFunction(); } 
}; 

任何類可以採取的是將其可見的任何成員,並決定公開揭露它。這樣做可能是糟糕的課堂設計,但這絕對不是該語言的缺陷。私人或受保護成員的整個觀點是,課程本身必須決定誰應該訪問該成員。如果班級決定「我要讓全世界都能進入」,那麼這就是班級的設計。

如果我們遵循你的邏輯,那麼getter和setter也會違反封裝。有時候他們會這樣做。但不是因爲語言被破壞。只是因爲你選擇設計破碎的類。

通過讓會員受到保護,您可以給派生類自由做他們喜歡與會員。他們可以看到它,所以他們可以修改它,或公開暴露它。您選擇在使會員受到保護時使其成爲可能。如果你不想這樣做,你應該把它變成私人的。

1

就我個人而言,我認爲這個問題應該作爲更多的設計問題而不是技術問題來解答。

我會問,「首先保護的方法的要點是什麼?」這是一種只有子類應該調用的方法,還是子類應該重寫的方法?然而,它可能是一種在泛型基類中不期望的方法,但也可能是在子類中預期的方法。對於基類的客戶,他們從來不知道那種受保護的方法。子類的創建者選擇擴大合同,現在受保護的方法是公開的。這個問題真的不應該是C++允許的,但是對於你的階級,合同和未來的維護者來說是否正確。當然,這可能是一種難聞的氣味,但是真的需要爲所涉及的用例做出正確的選擇。

如果您確實公開了受保護的方法,那麼請確保您爲維護者正確提供了內部文檔,以說明爲何做出此特定決定的理由。

一般來說,爲了安全起見,如前面提到的貢獻者,您可能希望在子類中使用委託函數(使用不同的名稱)。所以,而不是「get(int)」你可能有「getAtIndex(int)」或其他東西。這使您可以更輕鬆地重構/保護/抽象/將來的文檔。