我有2个资源管理类DeviceContext
和OpenGLContext
都是class DisplayOpenGL
的成员。资源生命周期与DisplayOpenGL
相关联。初始化看起来像这样(伪代码):
DeviceContext m_device = DeviceContext(hwnd);
m_device.SetPixelFormat();
OpenGLContext m_opengl = OpenGLContext(m_device);
问题是调用SetPixelFormat(),因为我无法在DisplayOpenGL
c'tor的初始化列表中执行此操作:
class DisplayOpenGL {
public:
DisplayOpenGL(HWND hwnd)
: m_device(hwnd),
// <- Must call m_device.SetPixelFormat here ->
m_opengl(m_device) { };
private:
DeviceContext m_device;
OpenGLContext m_opengl;
};
我可以看到的解决方案:
m_dummy(m_device.SetPixelFormat())
- 无法工作,因为SetPixelFormat()没有retval。 (如果它有一个retval,你应该这样做吗?)unique_ptr<OpenGLContext> m_opengl;
代替OpenGLContext m_opengl;
然后初始化为m_opengl()
,在c'tor正文中调用SetPixelFormat()并使用m_opengl.reset(new OpenGLContext);
SetPixelFormat()
c'tor DeviceContext
哪种解决方案更可取?为什么?我缺少什么?
我在Windows上使用Visual Studio 2010 Express,如果重要的话。
编辑:我最感兴趣的是在决定使用其中一种方法时所涉及的权衡。
m_dummy()
不起作用,即使它会unique_ptr<X>
对我来说很有趣 - 我何时会使用它而不是“普通”X m_x
成员?除初始化问题外,这两种方法似乎在功能上或多或少相同。SetPixelFormat()
c'tor调用DeviceContext
肯定有效,但对我来说感觉不洁净。 DeviceContext
应管理资源并启用其使用,而不是对用户施加一些随机像素格式策略。InitDev()
看起来是最干净的解决方案。在这种情况下,我是否总是想要一个基于智能指针的解决方案呢?
答案 0 :(得分:15)
Comma operator to the rescue!表达式(a, b)
将首先评估a
,然后评估b
。
class DisplayOpenGL {
public:
DisplayOpenGL(HWND hwnd)
: m_device(hwnd),
m_opengl((m_device.SetPixelFormat(), m_device)) { };
private:
DeviceContext m_device;
OpenGLContext m_opengl;
};
答案 1 :(得分:4)
在这种情况下,我是否总是想要一个基于智能指针的解决方案呢?
没有。避免这种不必要的并发症。
两个未提及的直接方法:
方法A:
干净的方式。
为m_device
存储创建一个小容器对象,在构造函数中调用SetPixelFormat()
。然后将DisplayOpenGL ::m_device
替换为该类型的实例。获得初始化顺序,意图非常明确。插图:
class DisplayOpenGL {
public:
DisplayOpenGL(HWND hwnd)
: m_device(hwnd),
m_opengl(m_device) { }
private:
class t_DeviceContext {
public:
t_DeviceContext(HWND hwnd) : m_device(hwnd) {
this->m_device.SetPixelFormat();
}
// ...
private:
DeviceContext m_device;
};
private:
t_DeviceContext m_device;
OpenGLContext m_opengl;
};
方法B:
快速&amp;脏的方式。在这种情况下,您可以使用静态函数:
class DisplayOpenGL {
public:
DisplayOpenGL(HWND hwnd)
: m_device(hwnd),
m_opengl(InitializeDevice(m_device)) { }
private:
// document why it must happen this way here
static DeviceContext& InitializeDevice(DeviceContext& pDevice) {
pDevice.SetPixelFormat();
return pDevice;
}
private:
DeviceContext m_device;
OpenGLContext m_opengl;
};
答案 2 :(得分:1)
在这里使用uniqe_ptr似乎是合适的:你可以转发声明DeviceContext和OpenGLContext,而不是包括它们的标题,即a good thing)。然后这工作:
class DisplayOpenGL
{
public:
DisplayOpenGL( HWND h );
private:
unique_ptr<DeviceContext> m_device;
unique_ptr<OpenGLContext> m_opengl;
};
namespace
{
DeviceContext* InitDev( HWND h )
{
DeviceContext* p = new DeviceContext( h );
p->SetPixelFormat();
return p;
}
}
DisplayOpenGL::DisplayOpenGL( HWND h ):
m_device( InitDev( h ) ),
m_opengl( new OpenGLContext( *m_device ) )
{
}
如果你可以使用c ++ 11,你可以用lambda替换InitDev()。
答案 3 :(得分:1)
如果OpenGLContext
具有0参数构造函数和复制构造函数,则可以将构造函数更改为
DisplayOpenGL(HWND hwnd)
: m_device(hwnd)
{
m_device.SetPixelFormat();
m_opengl = OpenGLContext(m_device);
};
unique_ptr
通常用于您希望将其中一个成员设为可选或“可空”时,您可能想要或可能不想在这里做。
答案 4 :(得分:1)
首先,你做错了。 :-)在构造函数中执行复杂的操作是非常糟糕的做法。永远。在必须传递给构造函数的辅助对象上创建这些操作函数。更好的是在类之外构造复杂对象并将它们完全传递给它们,这样如果你需要将它们传递给其他类,你也可以同时在它们的构造函数中这样做。再加上你有机会发现错误,添加合理的记录等等。
class OpenGLInitialization
{
public:
OpenGLInitialization(HWND hwnd)
: mDevice(hwnd) {}
void SetPixelFormat (void) { mDevice.SetPixelFormat(); }
DeviceContext const &GetDeviceContext(void) const { return mDevice; }
private:
DeviceContext mDevice;
};
class DisplayOpenGL
{
public:
DisplayOpenGL(OpenGLInitialization const &ogli)
: mOGLI(ogli),
mOpenGL(ogli.GetDeviceContext())
{}
private:
OpenGLInitialization mOGLI;
OpenGLContext mOpenGL;
};
答案 5 :(得分:0)
如果它属于DeviceContext
(从您的代码中看起来如此),请从DeviceContext
c&#39}调用它。
答案 6 :(得分:0)
将Comma operator与IIFE (Immediately-Invoked Function Expression)结合使用,这样您就可以使用逗号运算符定义变量和其他复杂内容:
struct DisplayOpenGL {
DisplayOpenGL(HWND hwnd)
: m_device(hwnd)
, opengl(([&] {
m_device.SetPixelFormat();
}(), m_device))
DeviceContext m_device;
OpenGLContext m_opengl;
};
答案 7 :(得分:0)
逗号运算符在你的情况下会做得很好,但我认为这个问题是你的课程计划不好的结果。我要做的是让构造函数只初始化对象的状态而不是依赖关系(例如OpenGL渲染上下文)。我假设OpenGLContext的构造函数初始化OpenGL渲染上下文,这是我不做的事情。相反,我为OpenGLContext类创建方法CreateRenderingContext
以进行初始化,并调用SetPixelFormat
class OpenGLContext {
public:
OpenGLContext(DeviceContext* deviceContext) : m_device(deviceContext) {}
void CreateRenderingContext() {
m_device->SetPixelFormat();
// Create the rendering context here ...
}
private:
DeviceContext* m_device;
};
...
DisplayOpenGL(HWND hwnd) : m_device(hwnd), m_opengl(&m_device) {
m_opengl.CreateRenderingContext();
}