【问题标题】:C++ Communicating private data from opaque pointerC ++从不透明指针通信私有数据
【发布时间】:2013-09-07 20:54:48
【问题描述】:

我最近了解了 C++ 中的不透明指针。我已经开始使用它们来隐藏特定于平台的私有成员。比如引用<windows.h>等中的定义。

现在,我有几个相互构建并需要相互通信的系统。例如,Direct3D 需要一个窗口句柄 (HWND)。我不想将平台定义暴露给我的核心系统,但是我的子系统需要传达这些数据。

我公开了不透明的数据并允许通过 void 指针进行访问。这允许访问所有私人数据。

示例用法(main.cpp):

// System:: namespace is my platform specific code
System::Window window;
System::DirectX::Direct3D9 gfxcontext(window);

窗口定义(System/Window.h):

class Window
{
    WindowData* data; // Opaque pointer
public:
    void* getHandle() const; // returns an HWND handle
    Window();
    ~Window();
}

如何检索有用数据(Direct3D9.cpp):

#include "Window.h"

Direct3D9::Direct3D9(const Window& _window)
{
    HWND windowHandle = *(HWND*)_window.getHandle();
    // [...]
    pp.hDeviceWindow = windowHandle;
}

但是,此代码有效!:

*(HWND*)_window.getHandle() = 0; // changes HWND in WindowData to NULL!

有没有办法在子系统之间传递平台特定信息而不将其暴露给我的独立代码 - 并保持私有数据的私密性?


编辑当前的WindowData实现

struct Window::WindowData
{
    static LRESULT CALLBACK MessageHandler(HWND, UINT, WPARAM, LPARAM);
    HWND windowHandle;
    WNDCLASSEX windowClass;
    HINSTANCE processInstance;
};

DirectX 在演示参数中使用 HWND (D3DPRESENT_PARAMETERS::hDeviceWindow)

【问题讨论】:

  • 您究竟是如何在这段代码中存储HWND 的?您可以采取稍微不同的方法。我倾向于pImpl 解决方案。
  • 垫子,我添加了 WindowData 代码——其中包括 HWND。我虽然不透明的指针是疙瘩...

标签: c++ architecture opaque-pointers


【解决方案1】:

我会让getHandle(或者更好的getWindowData返回WindowData *而不是void *。然后让WindowData只是“System/Window.h”文件中的前向声明。

在“Direct3D9”中,使用WindowData的完整定义,所以:

HWND hwnd = _window.getWindowData()->windowHandle;

如果在以后的某个阶段,您移植到 Linux,您可以在 WindowData 内部拥有一个完全不同的结构 [基于实现方面的一些 #ifdef __WIN32/#else 类型的结构]。

【讨论】:

  • 我最初尝试过这个。但是,因为我一开始就认为WindowDataWindow 的私人信息,所以公开所有我的私人数据感觉不对。我通过将Window 设置为friendWindowData 并公开Window::data 解决了这个问题。这允许Window 访问WindowData 中的私有字段,而子系统可以访问公共字段。由于我以前从未使用过友谊,我不确定这是否是最好的解决方案。
  • 如果不完全理解“Window”应该做什么(或者它是如何做的),很难准确地说出什么是“最佳”解决方案。一般来说,友谊应该是有限的。最好公开提供正确内容的函数。例如。 _window.GetWindowData()->GetHandle(),也许?
  • Window 是一个创建和显示 GUI 窗口的类...我的 System 命名空间中的所有内容都实现了文档化的外部接口,而内部实现了特定于平台的代码。这个想法是WindowDataWindow 的“扩展”。每个“pImpl”都有一个“所有者”,对于该所有者来说是friend,这似乎不是问题。 _window.GetWindowData()->GetHandle() 似乎会添加太多抽象层。然而我在WindowData 中实现的任何东西都需要进一步的类型安全和权限来限制依赖。访问者似乎填补了这个角色。
【解决方案2】:

在功能上定义你需要做什么,然后在接口方面实现它。不要暴露(公开)指针。此后,根据 HWND 和平台特定 API 实现接口。

例如:

struct WindowHandleImpl
{
  virtual void show() = 0;
  virtual void maximize() = 0;
  //etc...
};

struct Win32WinHandleImpl : WindowHandleImpl
{
  std::unique_ptr<HWND> handle_; //Use deleter...
  virtual void show(); //In terms of HWND, using Win32 API
  virtual void maximize();
};

struct XWinHandleImpl : WindowHandleImpl
{
  //In terms of platform specific handle.
};

struct Window
{
  void show(); //In terms of WindowHandleImpl
  void maximize();//In terms of WindowHandleImpl
  private:
    std::unique_ptr<WindowHandleImpl> pimpl_;
};

Window::Window( const Factory& factory )
: pimpl_( factory.createWindow() )
{
}
//or 
Window::Window()
: pimpl_( SystemFactory::instance().createWindow() )
{
}

【讨论】:

  • 请详细说明,要么我不明白,要么这是常识。我正在实现我想要的各种操作,例如切换全屏、最小/最大化窗口等。但是,其他子系统可能需要HWNDLPDIRECT3DDEVICE9、音频 I/O 句柄等。我不想公开这些是我的“核心”,但绝对允许子系统访问它。
  • 我没有实现我自己的窗口系统,但我之前已经实现/封装了线程库,用于各种操作系统/平台的 TCP/UDP/串行通信库。在这些努力中,我从未需要公开 HWND/线程句柄等。封装变化的概念 - 如果需要共享句柄,则需要根据特定的“功能”责任来共享它。
  • 责任/要求的存在与句柄是否存在无关(或不存在 - 对于某些平台)。如果您需要共享句柄,请通过定义其职责的接口共享它。最终,所有接口都在一个实现中达到高潮,每个共享者根据它定义的职责(或接口)共享句柄(通过 shared_ptr)。
【解决方案3】:

您可以复制数据并返回一个 unique_ptr。或者您可以将 HWND 作为 void* 而不是 HWND* 返回,因为它只是一个指针,尽管这确实利用了实现。但请记住,其他人仍然可以通过 HWND 以某种方式更改您的窗口,我想您对此无能为力。

【讨论】:

  • 问题不在于隐藏实际数据,而在于抽象出(特定于平台的)实现细节。
  • Cooky451,您对 HWND 的看法是正确的,我无法保护它。我在上面的例子中使用了(void*)。然而,铸造变得复杂。我一直在尝试将输出包装在另一个不透明指针中,但我还没有完全明白。我不确定有很多小结构/类来包装东西是否是好的设计。
猜你喜欢
  • 2013-07-12
  • 2011-02-12
  • 2020-05-17
  • 2011-07-15
  • 1970-01-01
  • 2016-01-15
  • 2016-06-30
  • 1970-01-01
相关资源
最近更新 更多