c++ - 如何在编译时更改类继承的内容?

标签 c++

在创建跨平台 GUI 框架的过程中,我遇到了以下障碍: 假设我有一个中央“Window”类,在项目的通用、平台无关的包含文件夹中:

//include/window.hpp
class Window
{
    //Public interface
}

然后我有几个平台相关的实现类,像这样:

//src/{platform}/window.hpp
class WinWindow {...}; //Windows
class OSXWindow {...}; //OSX
class X11Window {...}; //Unix

最后,还有原始窗口类的.cpp 文件,我想在其中将实现类“绑定(bind)”到通用类。纯粹从概念上讲,这就是我希望能够做到的:

//src/window.cpp
//Suppose we're on Windows

#include "include/window.hpp"
#include "src/win/window.hpp"
class Window : private WinWindow; //Redefine Window's inheritance

我知道这绝不是有效的 C++,这就是重点。我想到了两种可能的方法来解决这个问题,但我都遇到了问题。

pImpl 风格的实现

让 Window 持有一个指向实现类的 void 指针,并将其分配给每个平台的不同窗口类。但是,每次我想执行平台相关操作时,我都必须向上转换指针,更不用说在各处包含平台相关文件了。

预处理器指令

class Window :
#ifdef WIN32
private WinWindow
#else ifdef X11
private X11Window //etc.

然而,这听起来更像是黑客而不是问题的实际解决方案。

要做什么?我应该完全改变我的设计吗?我的任何可能的解决方案都含有一点水分吗?

最佳答案

使用typedef隐藏预处理器

您可以简单地键入定义适当的窗口类型:

#ifdef WINDOWS
    typedef WinWindow WindowType;
#elif defined // etc

那么你的窗口类可以是:

class Window : private WindowType {
};

不过,这不是一个非常可靠的解决方案。最好以更面向对象的方式思考,但 C++ 中的 OO 编程会带来运行时成本,除非您使用

奇怪的重复模板模式

您可以使用 curiously repeating template pattern :

template<class WindowType>
class WindowBase {
public:
    void doSomething() {
        static_cast<WindowType *>(this)->doSomethingElse();
    }
};

那你可以做

class WinWindow : public WindowBase<WinWindow> {
public:
    void doSomethingElse() {
        // code
    }
};

并使用它(假设 C++ 14 支持):

auto createWindow() {
#ifdef WINDOWS
    return WinWindow{};
#elif UNIX
    return X11Window{};
#endif
}

仅适用于 C++ 11:

auto createWindow()
    ->
#ifdef WINDOWS
    WinWindow
#elif defined UNIX
    X11Window
#endif
{
#ifdef WINDOWS
    return WinWindow{};
#elif defined UNIX
    return X11Window{};
#endif
}

我推荐你在使用的时候使用auto,或者结合typedef使用:

auto window = createWindow();
window.doSomething();

面向对象风格

你可以让你的 Window 类成为一个抽象类:

class Window {
protected:
    void doSomething();
public:
    virtual void doSomethingElse() = 0;
};

然后将您的平台相关类定义为Window 的子类。然后您所要做的就是将预处理器指令放在一个地方:

std::unique_ptr<Window> createWindow() {
#ifdef WINDOWS
    return new WinWindow;
#elif defined OSX
    return new OSXWindow;
// etc
}

不幸的是,这会通过调用虚拟函数产生运行时成本。 CRTP 版本在编译时而不是在运行时解析对“虚函数”的调用。

此外,这需要在堆上声明 Window 而 CRTP 不需要;这可能是一个问题,具体取决于用例,但一般来说,这并不重要。


最终,您必须在某处使用#ifdef,这样您才能确定平台(或者您可以使用确定平台的库,但它可能使用#ifdef 也是),问题是把它藏在哪里。

关于c++ - 如何在编译时更改类继承的内容?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38775430/

相关文章:

c++ - new 和 delete[] 比 malloc 和 free 差吗? (c++/VS2012)

c++ - 在 C++ 中使用枚举进行 for 循环

c++ - 如何读锁多线程C++程序

C++ SDL,正在使用未初始化的变量

c++ - 如何判断输入的数字是否平衡?

c++向网络服务器上的API发送请求然后接收json数组响应?

c++ - 围绕显式模板实例化的困惑

c++ - 使用模型作为 QMenu 的源

c++ - gcc/clang 是否有可能在剥离所有调试信息的同时链接到可执行文件?

c++ - 如何在动态矩阵中创建主键