1

(我在这里搜索并阅读了 Diamond-and virtual-inheritance questions,但找不到答案。我的想法是这种情况有点不寻常,我愿意接受我的要求有些偏离的想法。关于另一方面,我认为这应该以“不错”的方式可行。)

情况及要求:

我有一个我无法控制且无法更改的 C++ 类库。它定义了一个Window类。该类Window有一个受保护的成员(例如:)handle,不能以其他方式访问,派生类打算使用该成员。Window定义了数百个(嗯,非常多的......)方法,我不想通过委托(比如在装饰器中)重新实现这些方法。

我想向 中添加功能Window,以便派生类(我写的;说LogWindow)自动具有。此类功能的一个示例是能够将窗口彼此对齐。为了实现这一点,我需要访问Window受保护的handle成员。

对于我的实际目的,这已经足够了,解决方案很简单:派生SnappableWindowWindow,并派生所有我的Window派生类(LogWindow在本例中)从SnappableWindow

然而,我真正想要的,更漂亮的恕我直言,是:

  1. 将这种“Snappable”功能作为独立代码片段的能力,我可以选择“插入”任何其他Window派生类,也可以不选择。
  2. 将这一概念扩展到其他功能的能力,例如最小化窗口的能力。所以我可以有一个Window- 派生类,有或没有“Snappable”能力,有或没有“Minimizable”能力。
  3. “SnappableWindow”和“MinimizableWindow”的实现都需要访问受保护Windowhandle成员。
  4. 我希望“Snappable”和“Minimizable”成为实际类声明的一部分,这样我的实际类(LogWindow)“是一个”窗口,“是一个”SnappableWindow,并且“是一个”MinimizableWindow。

...现在是问题:

我知道如何通过声明SnappableWindowandMinimizableWindow不是从它们的构造函数中Window获取 a来执行此操作handle,然后LogWindowWindowand 的任意组合派生SnappableWindow和派生MinimizableWindow

编辑:在调用了init()之后,在 LogWindow 的构造函数中途handle被初始化。(而不是像我之前所说的那样,通过构造函数的一半)。WindowWindowWindow

但是,由于handleLogWindow在 's 的构造函数中初始化(在它调用Window's之后init()),我不能将 is 传递给SnappableWindowMinimizableWindow作为LogWindow's 的构造函数初始化列表的一部分。相反,我必须init()在两者上显式调用一些方法,并将其传递给handle. 而这,在我的每个Window派生类中。( LogWindow, SearchWindow,PreferencesWindow等)

我正在寻找一种能够执行以下操作的方法:

类 LogWindow:公共窗口、公共 SnappableWindow、公共 MinimizableWindow

...并且不必在内部实现任何其他内容LogWindow。我已经摆弄虚拟继承,但不能完全想出解决方案。

4

3 回答 3

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
于 2012-07-13T22:13:37.013 回答
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> {
};

在这里寻找一种使用特征的有趣的建筑风格。

于 2012-07-14T01:33:59.900 回答
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
}

重要的是要注意基本构造函数的执行顺序是类定义中声明的顺序,而不是初始化列表中的顺序。

话虽如此,这是否是一个好的设计是一个不同的问题。

于 2012-07-13T21:50:34.207 回答