1

(我搜索和閱讀直通這裏的金剛石和虛擬繼承問題,但找不到答案。我的想法是,這種情況有點反常,我。願意接受的想法,我的要求是有點不同。另一方面,我想這應該在一個「好」的方式是可行的)C++「三角」(而不是鑽石)繼承

的情況和要求:

我有一個C++類我無法控制的圖書館,以及我無法改變的圖書館。它定義了一個Window類。 Window類有一個受保護的成員(說:handle),無法以其他方式訪問,派生類將用於使用。 Window定義了數百個(以及非常大數量的)方法,我不關心通過委託來重新實現(例如在裝飾器中)。

我想添加功能Window,以便派生類(我寫;說LogWindow)自動具有。這種功能的一個例子是能夠將窗口彼此對齊。爲了實現這一點,我需要訪問Window的受保護的handle成員。

對於我的現實生活的目的,這是不夠的,解決方法很簡單:獲得來自WindowSnappableWindow,並從SnappableWindow帶來的一切我Window派生類(LogWindow在這個例子中)。

不過,我想真的想要的東西,而且是漂亮恕我直言,是:

  1. 有這個「Snappable」功能作爲一個獨立的代碼,我可以選擇「插件的能力成「任何其他Window派生類,或不。
  2. 也可以將這個概念擴展到其他功能,例如最小化窗口的能力。所以我可以有Window派生的職業,有或沒有「Snappable」能力,有或沒有「最小化」能力。
  3. 「SnappableWindow」和「MinimizableWindow」的實現都需要訪問Windowhandle保護成員。
  4. 我想將「Snappable」和「Minimizable」作爲實際類聲明的一部分,這樣我的實際類(LogWindow)就是一個「Window」,它是一個「SnappableWindow,」是一個「MinimizableWindow」。

...現在的問題:

我得到我如何與聲明SnappableWindowMinimizableWindowWindow推導,但在其構造而獲得一個handle,然後得出這樣做LogWindow,從WindowSnappableWindowMinimizableWindow的任意組合。

編輯:handle初始化,在Window,中途通過LogWindow的構造函數,它調用Window的init()後。 (而不是前面說過的Window的構造函數的一半)。

然而,因爲handle只有一半初始化方式通過LogWindow的構造函數(它叫Window後的init()),我能不能通過是SnappableWindowMinimizableWindowLogWindow的構造函數初始化列表的一部分。相反,我必須明確地調用一些init()方法,並將其傳遞給handle。這在我的每個Window派生的類中。 (LogWindowSearchWindowPreferencesWindow等)

我正在尋找一種方法,以便能夠像做:

類LogWindow:公共窗口,公共SnappableWindow,公共MinimizableWindow

...並且不需要在LogWindow內執行其他任何操作。我已經擺弄虛擬繼承,但無法完全解決這個問題。

+0

也許你可以看看http://en.wikipedia.org/wiki/Aspect-oriented_programming – 2012-07-13 21:55:02

+0

至少在Windows上,WTL已經非常類似於你似乎試圖做的事情。由於派生構造函數只在基類構造函數完成後運行*,因此在基類的構造函數中途初始化的句柄不應引起問題。 – 2012-07-13 22:01:08

回答

1

虛繼承應該能夠處理這個問題:

class SnappableWindow: virtual public Window 

class MinimizableWindow: virtual public Window 

class LogWindow: virtual public Window, public SnappableWindow, public MinimizableWindow 

注意三角形只是鑽石的特殊情況!

Window 
| \ \---------------\ 
| \     \ 
| SnappableWindow MinimizableWindow 
| /    /
|/ /-------------/ 
LogWindow 

編輯:這裏有一個完整的例子:

#include <iostream> 
int get_handle() { static int handle = 0; return ++handle; } 
struct Window { 
    int handle; 
    Window() : handle(get_handle()) { } 
}; 
struct SnappableWindow: virtual public Window { 
    SnappableWindow() { std::cout << "Snap! " << handle << std::endl; } 
}; 
struct MinimizableWindow: virtual public Window { 
    MinimizableWindow() { std::cout << "Mm! " << handle << std::endl; } 
}; 
struct LogWindow: virtual public Window, public SnappableWindow, public MinimizableWindow { 
    LogWindow() { std::cout << "Log! " << handle << std::endl; } 
}; 
int main() { 
    LogWindow lw; 
    std::cout << "lw: " << lw.handle << std::endl; 
} 

輸出:

Snap! 1 
Mm! 1 
Log! 1 
lw: 1 
+0

謝謝,就是這樣。我的問題是我在LogWindow類中幾乎沒有從窗口派生出來。我實際上從窗口派生了SnappableWindow。我認爲這很好回答。你會說這是「好的」和一個可接受的模式嗎?其他更好的方法是什麼? – 2012-07-13 22:24:15

+0

@NitzanShaked虛擬繼承是絕對正確的;瞭解基類偏移量在運行時應用是很重要的,因此性能會受到很小的影響,並且與改進的二進制兼容性相平衡。 – ecatmur 2012-07-15 22:09:48

+0

Jordão在運行時使用CRTP的建議更爲高效,因爲在編譯時烘焙了基類偏移量;這是通過增加編譯和代碼大小的開銷來平衡的。這兩種方法都是公認的實現mixin式功能的方式。 – ecatmur 2012-07-15 22:20:01

0

這實際上是一個有點混亂......如果手柄爲Window構造的一部分進行初始化,那麼它會Window構造函數初始化列表完成後可用:

class Window { 
protected: 
    int handle; 
    Window() { handle = 5; } 
}; 
struct Snappable { 
    int hdl; 
    Snappable(int handle) : handle(handle) {} 
}; 
struct MyWindow : Window, Snappable {  // Order matters here! 
    MyWindow() : Window(), Snappable(handle) {} 
}; 
int main() { 
    MyWindow w; 
    std::cout << w.hdl << std::endl;   // 5 
} 

這是很重要請注意基礎構造函數的執行順序與類定義中聲明的順序不同,而不是初始化程序列表中的順序。

這就是說,這是不是一個好的設計是一個不同的問題。

+0

對於描述「句柄」初始化的錯誤,很抱歉。我編輯了原始問題。 – 2012-07-13 22:03:05

1

您可以使用特徵爲,但不幸的是,他們無法訪問受保護的成員。如果您創建公開受保護成員的中間類,則可以這樣做。看它是否有道理:

struct Window { 
protected: 
    int handle; 
}; 
struct BaseWindow : public Window { 
    int get_handle() { return handle; } 
}; 
template <class TWindow> 
struct Snappable { 
    Snappable() { std::cout << "Snappable " << self()->get_handle() << std::endl; } 
private: 
    TWindow *const self() { 
     return static_cast<TWindow*>(this); 
    } 
}; 
template <class TWindow> 
struct Minimizable { 
    Minimizable() { std::cout << "Minimizable " << self()->get_handle() << std::endl; } 
private: 
    TWindow *const self() { 
     return static_cast<TWindow*>(this); 
    } 
}; 
struct LogWindow: public BaseWindow, public Snappable<LogWindow>, public Minimizable<LogWindow> { 
}; 

here爲使用性狀一個有趣的建築風格。

+0

不錯,謝謝你。 – 2012-07-14 07:23:34

+0

不客氣! – 2012-07-15 00:50:45

+0

此模式 - 派生類模板 - 簡稱爲http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern或CRTP。 – ecatmur 2012-07-15 22:11:19