C++ "triangle"(而非钻石)继承
C++ "triangle" (and not diamond) inheritance
(我在这里搜索并阅读了Diamond和虚拟继承的问题,但找不到答案。我的想法是这种情况有点不寻常,我愿意接受我的需求有点偏离的想法。另一方面,我认为这应该以一种"好"的方式可行。)
情况和要求:
我有一个C++类库,我无法控制它,也无法更改它。它定义了一个Window
类。Window
类有一个受保护的成员(例如:handle
),该成员不可访问,派生类将使用该成员。Window
定义了数百个(嗯,数量很大…)方法,我不想通过委派(比如在装饰器中)来重新实现这些方法。
我想为Window
添加功能,以便派生类(我编写的,比如LogWindow
)自动拥有。这种功能的一个示例是能够将窗口彼此捕捉。为了实现这一点,我需要访问Window
的受保护的handle
成员。
对于我现实生活中的目的来说,这就足够了,解决方案很简单:从Window
派生SnappableWindow
,并从SnappableWindow
派生我所有的Window
派生类(本例中为LogWindow
)。
然而,我真正想要的,也是更漂亮的IMHO,是:
- 能够将这种"Snappable"功能作为一段独立的代码,我可以选择将其"插入"任何其他
Window
派生的类,也可以不插入 - 将这一概念扩展到其他功能的能力,例如最小化窗口的能力。因此,我可以有一个
Window
派生的类,带有或不带有"Snappable"功能,以及带有或不具有"Minimizable"功能 - "SnappableWindow"answers"MinimizableWindow"的实现都需要访问
Window
的handle
受保护成员 - 我希望"Snappable"answers"Minimizable"是实际类声明的一部分,这样我的实际类(
LogWindow
)"就是一个"Window",是一个"SnappableWindow",也是一个"MinimizableWindow"
现在是问题:
我知道如何将SnappableWindow
和MinimizableWindow
声明为而不是从Window
派生的,而是在它们的构造函数中获得handle
,然后从Window
以及SnappableWindow
和MinimizableWindow
的任何组合派生LogWindow
。
EDIT:handle
在调用Window
的init()之后,在LogWindow的构造函数的中途,在Window
中初始化。(正如我之前所说,还没有到Window
构造函数的一半)。
但是,由于handle
只在LogWindow
的构造函数的一半处初始化(在它调用了Window
的init()
之后),所以我不能将is作为LogWindow
的构造函数初始化列表的一部分传递给SnappableWindow
和MinimizableWindow
。相反,我必须在两者上显式调用一些init()
方法,并将其传递给handle
。在我的每个Window
派生类中。(LogWindow
、SearchWindow
、PreferencesWindow
等)
我正在寻找一种能够做一些事情的方法,比如:
类LogWindow:公共窗口,公共SnappableWindow,公共MinimizableWindow
并且不必在CCD_ 44内实现任何其它功能。我对虚拟继承进行了一些处理,但还不能完全想出解决方案。
虚拟继承应该能够处理这个问题:
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
您可以使用traits,但不幸的是,它们无法访问受保护的成员。如果您创建一个公开受保护成员的中间类,则可以执行此操作。看看它是否有意义:
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> {
};
在这里寻找一种有趣的建筑风格,它使用了特性。
这实际上有点令人困惑。。。如果句柄被初始化为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
}
需要注意的是,基构造函数的执行顺序是类定义中声明的顺序,而不是初始值设定项列表中的序列。
话虽如此,这是否是一个好的设计是另一个问题。
- 钻石继承虚拟成员铸造与指针
- 接口的钻石继承(C++)
- c++ 中的函数重载如何在没有钻石继承的情况下工作?
- C++解决没有虚拟继承的钻石继承问题
- 如何调用所有基本类的复制构造函数,以在C 中复制钻石继承中最派生的类对象
- 假设钻石继承打破了C++的封装是否正确?
- C++多个钻石继承和纯虚函数
- 首选钻石继承中一个类的变量
- 钻石继承,C++ 处理
- 将 Boost.Serialization 直接与虚拟钻石继承一起工作
- 钻石继承最低基类构造函数
- 钻石继承方案在 G++ 中编译良好,但在 VC++/Eclipse 中产生警告/错误
- C++ "triangle"(而非钻石)继承
- 让钻石继承发挥作用,但Eclipse仍然抱怨
- 钻石继承与提升::绑定
- 为什么不使用只有一个虚拟继承的钻石继承呢
- 使用 Windows V2 凭据提供程序的钻石继承
- c++虚拟/非虚拟钻石继承
- 带有混合继承修饰符(protected/private/public)的钻石继承
- 避免钻石继承