26

2 つのリソース管理クラスがDeviceContextあり、OpenGLContextどちらも のメンバーですclass DisplayOpenGL。リソースの有効期間は に関連付けられていDisplayOpenGLます。初期化は次のようになります (疑似コード):

DeviceContext m_device = DeviceContext(hwnd);
m_device.SetPixelFormat();
OpenGLContext m_opengl = OpenGLContext(m_device);

DisplayOpenGL問題は SetPixelFormat() の呼び出しです。これは、 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()者からの電話DeviceContext

これらのソリューションのどれが望ましいですか?またその理由は? 不足しているものはありますか?

問題があれば、Windows で Visual Studio 2010 Express を使用しています。

編集:私は主に、これらの方法のいずれかを決定する際のトレードオフに関心があります。

  • m_dummy()機能しませんし、機能したとしてもエレガントではないようです
  • unique_ptr<X>は私にとって興味深いものです。「通常の」X m_xメンバーの代わりに使用するのはいつですか? 2 つの方法は、初期化の問題を除けば、機能的にほぼ同等のようです。
  • c'torSetPixelFormat()からの呼び出しはDeviceContext確かに機能しますが、私には不潔に感じます。DeviceContextユーザーにランダムなピクセル形式のポリシーを課すのではなく、リソースを管理してその使用を有効にする必要があります。
  • stijn's InitDev()は、最もクリーンなソリューションのように見えます。

とにかく、そのような場合、ほとんど常にスマートポインターベースのソリューションが必要ですか?

4

8 に答える 8

30

コンマ演算子が救助に!(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;
};
于 2012-11-09T19:18:57.590 に答える
6

とにかく、そのような場合、ほとんど常にスマートポインターベースのソリューションが必要ですか?

いいえ。この不必要な複雑さは避けてください。

言及されていない2つの即時のアプローチ:

アプローチ 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;
};
于 2012-11-09T20:55:02.840 に答える
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;
};
于 2012-11-10T06:10:45.013 に答える
1

OpenGLContext引数が 0 のコンストラクターとコピー コンストラクターがある場合は、コンストラクターを次のように変更できます。

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

unique_ptr通常、メンバーの 1 つをオプションまたは「nullable」にしたい場合に使用されます。これは、ここで行う場合と行わない場合があります。

于 2012-11-09T16:47:53.063 に答える
1

ここでは、両方に uniqe_ptr を使用するのが適切と思われます: ヘッダーを含める代わりに、DeviceContext と OpenGLContext を前方宣言できます。これは良いことです)。次に、これは機能します:

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 を使用できる場合は、InitDev() をラムダに置き換えることができます。

于 2012-11-09T15:54:49.557 に答える
0

コンマ演算子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;
};
于 2015-03-17T15:48:53.013 に答える
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();
}
于 2016-11-29T12:54:11.900 に答える
0

それが属している場合DeviceContext(そしてあなたのコードからそう思われる場合)、DeviceContextc'torから呼び出します。

于 2012-11-09T15:51:06.237 に答える