15

我有2個資源管理類DeviceContextOpenGLContext都是class DisplayOpenGL的成員。資源壽命與DisplayOpenGL有關。初始化看起來是這樣的(僞代碼):C++ - 在初始化類成員之前運行函數

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(),調用SetPixelFormat()在c'tor體和使用m_opengl.reset(new OpenGLContext);
  • 呼叫SetPixelFormat()DeviceContext c'tor

哪個這些解決方案的最好,爲什麼?我錯過了什麼?

我在Windows上使用Visual Studio 2010 Express,如果它很重要。

編輯:我最感興趣的是決定這些方法之一的權衡。

  • m_dummy()不工作,似乎不雅,即使它會
  • unique_ptr<X>有趣的是,我 - 當我會用它來代替「正常」 X m_x會員?除了初始化問題外,這兩種方法似乎在功能上或多或少是等同的。
  • DeviceContext調用SetPixelFormat() c'tor當然有效,但感覺對我不乾淨。 DeviceContext應該管理資源並啓用其使用,而不是對用戶強加一些隨機像素格式策略。
  • stijn'sInitDev()看起來像最乾淨的解決方案。

難道我幾乎總是想要一個基於智能指針的解決方案嗎?

+0

這似乎是那種情況,其中一個靜態工廠函數可能比一個構造函數更加有用。 – cdhowie

+0

在我看來,你喜歡你的第三種解決方案。你有沒有選擇不這樣做?另外,爲什麼不使用像GLFW這樣的庫來加載你的openGL上下文? –

+0

我開始時並不知道GLFW。會看看,謝謝。 –

回答

12

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; 
}; 
0

如果它屬於DeviceContext(它看起來如此從您的代碼),請從DeviceContext c'tor。

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()。

1

如果OpenGLContext有0參數的構造函數和拷貝構造函數,你可以在你的構造函數改爲

DisplayOpenGL(HWND hwnd) 
: m_device(hwnd) 
{ 
    m_device.SetPixelFormat(); 
    m_opengl = OpenGLContext(m_device); 
}; 

unique_ptr通常用於當你想成員可選或一個「空的,」您可能會或可能不想在這裏做。

3

難道我幾乎總是想在這種情況下,智能指針基礎的解決方案呢?

否避免這種不必要的複雜性。

還沒有被提到

兩個直接的方法:

解決方法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:

快速&骯髒的方式。在這種情況下,您可以使用靜態功能:

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; 
}; 
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; 
}; 
0

合併Comma operatorIIFE (Immediately-Invoked Function Expression),它可以讓你定義變量和其他複雜的東西只是逗號操作符不可用:

struct DisplayOpenGL { 
    DisplayOpenGL(HWND hwnd) 
     : m_device(hwnd) 
     , opengl(([&] { 
      m_device.SetPixelFormat(); 
     }(), m_device)) 
    DeviceContext m_device; 
    OpenGLContext m_opengl; 
}; 
0

逗號運算符在你的情況下會做的很好,但我認爲這個問題是你的類的糟糕計劃的結果。我要做的是讓構造函數只初始化對象的狀態而不是依賴項(例如OpenGL渲染上下文)。我假設OpenGLContext的構造函數初始化OpenGL渲染上下文,這就是我不想做的。相反,我想創建方法CreateRenderingContext爲OpenGLContext類做初始化,並調用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(); 
}