(我在这里通过钻石和虚拟遗传问题进行搜索和阅读,但找不到答案。我的想法是这种情况有点不寻常,我愿意接受我的要求有些不同的想法另一方面,我认为这应该以“好”的方式实现。)
情况和要求:
我有一个C ++类库,我无法控制,我无法改变。它定义了一个Window
类。 Window
类有一个受保护的成员(例如:handle
),而不是可访问的,派生类是要使用的。 Window
定义了数百个(好的,非常大的......)方法,我不想通过委托来重新实现(比如在装饰器中)。
我想向Window
添加功能,以便派生类(我写的;说LogWindow
)自动拥有。此类功能的一个示例是能够将窗口彼此捕捉。为了实现这一点,我需要访问Window
的受保护handle
成员。
对于我的现实目的,这已经足够了,而且解决方案很简单:从SnappableWindow
派生Window
,并派生所有Window
派生类(LogWindow
在这个例子中)来自SnappableWindow
。
然而,我真正想要的东西,以及更漂亮的恕我直言,是:
Window
派生类。Window
派生类,有或没有“Snappable”能力,有或没有“Minimizable”能力。Window
的{{1}}受保护成员。handle
)“是一个”Window“,是一个”SnappableWindow,并且“是一个“MinimizableWindow。......现在回答问题:
我知道如何通过将LogWindow
和SnappableWindow
声明为而不是从MinimizableWindow
派生,而是在他们的Window
中获得handle
构造函数,然后从LogWindow
以及Window
和SnappableWindow
的任意组合派生MinimizableWindow
。
编辑:handle
在调用Window
的init()后,在Window
中,通过LogWindow的构造函数初始化。 (正如我之前所说的那样,而不是通过Window
的构造函数的一半)。
但是,因为handle
仅在LogWindow
的构造函数中途初始化(在调用Window
的{{1}}之后) ,我无法传递给init()
和SnappableWindow
作为MinimizableWindow
的构造函数初始化列表的一部分。相反,我必须在两者上明确调用一些LogWindow
方法,并将init()
传递给它。而且,在我的每个handle
派生类中。 (Window
,LogWindow
,SearchWindow
等等。)
我正在寻找一种能够做到这样的事情的方法:
类LogWindow:public Window,public SnappableWindow,public MinimizableWindow
...而不必在PreferencesWindow
内实现任何其他内容。我已经摆弄了虚拟继承,但无法提出解决方案。
答案 0 :(得分: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
答案 1 :(得分: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有关使用特征的有趣建筑风格。
答案 2 :(得分: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
}
重要的是要注意基本构造函数的执行顺序是类定义中的声明的顺序,而不是初始化程序列表中的顺序。
话虽如此,这是否是一个好的设计是一个不同的问题。