2016-07-09 38 views
3

我遇到一個有趣的理論,我想知道是否有一個安全機制在C++反對此。繼承和朋友:安全機制?

class Base 
{ 
private: 
virtual void a(){}; 
friend class C; 
} 

class Derived: public Base 
{ 
void a() override {}; 
} 

class C{ 

public doSomething(const Base& b) {b.a();}; 

} 

所有這些都是合法的--C類是基地的朋友,因此可以調用b.a()。但是,當它收到一個引用時,它可以接收對Derived對象的引用,因此C將訪問派生類中的私有區域,而不是派生的朋友。

這是簡單的錯誤代碼設計還是有一個安全的防範呢?當爲基礎覆蓋運營商< <,並從內部調用通用打印功能(運營商是基地的朋友)時,發生在我身上。

+1

在C++中你有朋友,所以你可以在他們的私人部分。 – Mikhail

+0

可視化的方式是,派生類的訪問說明符適用於通過左值的靜態類型及其後代進行訪問 - 而不是追溯性地改變其基地聲明爲通過基本左值訪問的訪問說明符,最不好的,可能是荒謬的。 –

回答

1

作爲設計的骨架(Base和Derived),這是Herb Sutter 15 years ago提倡的典型優秀設計 - 除了讓C類朋友變得多餘,因爲它可以調用公共函數run( )。如果run()需要避免更多的功能(並且我會懷疑在這種情況下有一個糟糕的設計),那麼只需要只使用一個()就可以成爲朋友。 我想知道我的閱讀有什麼問題,因爲與以前的答案不同,我沒有看到a()是公開的,我也沒有看到該基礎是私人基礎。 享受

+0

答案中的鏈接提供了確切的解釋 - 我會建議讀給任何有興趣的人 –

4

在這裏沒有必要的保護措施。這就是C++如何通過設計工作的。

通過覆蓋基類中的虛函數,派生類重新實現它。但「重新實施」的定義包括從基類繼承所有的包袱。包括這種情況下基類中的虛函數爲public的事實。

派生類將重新實現函數定義爲private的事實不會改變它覆蓋公共函數的事實。因此,它應該期望它仍然可以通過基類到達。

我想最多可以稱之爲「怪癖」。最重要的是,如果你重寫了一個虛擬函數,重寫的過程並不會改變它是一個公共函數的事實。在派生類中使重寫函數「私有」在馬已經離開後關閉穀倉。

而且您必須意識到您重寫公共虛擬功能的事實。在某些情況下,我會說這甚至可能是一個功能。不難想象,只能由直接引用基類的現有代碼調用特定基類函數的情況。任何具有指向派生類的指針或引用的代碼都是更高級別的代碼,不應該有任何業務調用基類的函數。當然,這將是一種方法。當然,這並不意味着某種類型的密碼安全,而只是編譯器幫助您捕捉邏輯或功能錯誤或設計某種錯誤的一種機制。

+0

我認爲一個更簡單的方法來顯示這一點是,派生類的訪問說明符適用於通過左值的靜態類型及其後代的訪問,而不是追溯性地能夠改變其基礎的規範,這當然是荒謬的。 –

+0

而... *(聳肩)*「什麼是'朋友'?」 ......是的,他們是那些把你從派對帶回家的人,然後假裝第二天你從未醉過。在C++中,「朋友」的概念是:「我們可以*信任的某個人,通過一個點頭和一個知道的眨眼。」一個「朋友」是......我們應該說......有點像一個同胞*一個「有時非常必要的邪惡......」 –

+0

C++類的「朋友」非正式地是類信任足以允許無限制地訪問其所有私有,受保護和公共部分。所以,可以說,一個C++類相信所有的朋友比人們相信他們的更多。 – Peter

4

那麼,不,這是不錯的C++設計。這是你的程序設計不好。

這確實有點像

some_C.doSomething(some_derived); 

的程序將無法編譯。這是因爲你的BaseDerived的私人基地。所以將Derived &轉換爲Base &是無效的。

上述例外情況是,如果調用函數是成員或friendDerived。在這種情況下,行爲由(開發者)Derived控制,如果您已經完成了這樣的事情,則完全是您的責任。