15

Tôi có 2 lớp quản lý tài nguyên DeviceContextOpenGLContext cả hai đều là thành viên của class DisplayOpenGL. Thời gian hoạt động của tài nguyên được gắn với DisplayOpenGL. Khởi trông như thế này (pseudo code):C++ - Chạy một hàm trước khi khởi tạo thành viên lớp

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

Vấn đề là các cuộc gọi đến SetPixelFormat(), vì tôi không thể làm điều đó trong danh sách khởi tạo của 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; 
}; 

Giải pháp mà tôi có thể thấy:

  • Chèn m_dummy(m_device.SetPixelFormat()) - Sẽ không làm việc như SetPixelFormat() không có retval. (bạn nên làm điều này nếu nó có một khoản hoàn trả?)
  • Sử dụng unique_ptr<OpenGLContext> m_opengl; thay vì OpenGLContext m_opengl;.
    Sau đó khởi tạo như m_opengl(), gọi SetPixelFormat() trong cơ thể c'tor và sử dụng m_opengl.reset(new OpenGLContext);
  • Gọi SetPixelFormat() từ DeviceContext c'tor

nào trong các giải pháp này là một lợi thế và tại sao? Bất cứ điều gì tôi đang mất tích?

Tôi đang sử dụng Visual Studio 2010 Express trên Windows, nếu nó quan trọng.

Chỉnh sửa: Tôi chủ yếu quan tâm đến việc cân bằng liên quan đến việc quyết định một trong các phương pháp này.

  • m_dummy() không hoạt động và dường như không thanh nha ngay cả khi nó sẽ
  • unique_ptr<X> là thú vị với tôi - khi ta muốn sử dụng nó thay vì một "bình thường" X m_x thành viên? Hai phương pháp dường như có chức năng tương đương nhiều hoặc ít hơn, ngoại trừ các vấn đề khởi tạo.
  • Gọi SetPixelFormat() từ DeviceContext c'tor chắc chắn hoạt động nhưng cảm thấy ô uế với tôi. DeviceContext nên quản lý tài nguyên và cho phép sử dụng tài nguyên, không áp đặt chính sách định dạng pixel ngẫu nhiên cho người dùng.
  • stijn'sInitDev() trông giống như giải pháp sạch nhất.

Tôi có luôn muốn một giải pháp dựa trên con trỏ thông minh trong các trường hợp như vậy không?

+0

Điều này có vẻ giống như các loại trường hợp một chức năng nhà máy tĩnh có thể hữu ích hơn một constructor. – cdhowie

+0

dường như với tôi như giải pháp thứ ba của bạn sẽ hoạt động. Có lý do nào bạn chọn không? Ngoài ra, tại sao không sử dụng một thư viện như GLFW để tải ngữ cảnh OpenGL của bạn? –

+0

Tôi không biết GLFW khi tôi bắt đầu. Sẽ có một cái nhìn, cảm ơn. –

Trả lời

12

Comma operator to the rescue! Biểu thức (a, b) sẽ đánh giá a trước, sau đó 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

Nếu nó thuộc về DeviceContext (và có vẻ như từ mã của bạn), hãy gọi nó từ DeviceContext c'tor.

1

Sử dụng uniqe_ptr cho cả hai có vẻ thích hợp ở đây: bạn có thể chuyển tiếp khai báo DeviceContext và OpenGLContext, thay vì bao gồm tiêu đề của chúng, là a good thing). Sau đó, công trình này:

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)) 
{ 
} 

Nếu bạn có thể sử dụng C++ 11, bạn có thể thay thế InitDev() bằng lambda.

1

Nếu OpenGLContext có một constructor 0 đối số và copy constructor bạn có thể thay đổi nhà xây dựng của bạn để

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

unique_ptr thường được sử dụng khi bạn muốn thực hiện một trong những thành viên không bắt buộc hoặc "nullable", mà bạn có thể hoặc có thể không muốn làm ở đây.

3

Tôi có luôn muốn một giải pháp dựa trên con trỏ thông minh trong các trường hợp như vậy không?

No. Tránh biến chứng không cần thiết này.

cách tiếp cận

Hai ngay lập tức mà chưa được đề cập:

Cách tiếp cận A:

Cách sạch.

Tạo đối tượng vùng chứa nhỏ cho bộ nhớ m_device gọi SetPixelFormat() trong hàm tạo. Sau đó, thay thế DisplayOpenGL ::m_device bằng một phiên bản thuộc loại đó. Thứ tự khởi tạo thu được, và ý định khá rõ ràng. Minh họa:

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

Cách tiếp cận B:

Các nhanh & cách bẩn. Bạn có thể sử dụng hàm tĩnh trong trường hợp này:

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

Trước hết, bạn đang làm sai. :-) Thực hành rất kém để làm những điều phức tạp trong các nhà xây dựng. Không bao giờ. Làm cho các hàm hoạt động đó trên đối tượng trợ giúp phải được truyền vào hàm tạo. Tốt hơn là xây dựng các đối tượng phức tạp của bạn bên ngoài lớp của bạn và truyền chúng hoàn toàn được tạo ra, theo cách đó nếu bạn cần truyền chúng sang các lớp khác, bạn có thể làm như vậy trong các hàm tạo của THEIR cùng một lúc. Cộng với cách mà bạn có cơ hội phát hiện sai sót, bổ sung khai thác gỗ hợp lý vv

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

Kết hợp Comma operator với một IIFE (Immediately-Invoked Function Expression), cho phép bạn xác định các biến và các công cụ phức tạp khác không có sẵn chỉ với toán tử dấu phẩy:

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

Toán tử dấu phẩy sẽ làm khá tốt trong trường hợp của bạn nhưng tôi nghĩ rằng vấn đề này là hậu quả của việc lên kế hoạch xấu cho các lớp học của bạn. Những gì tôi muốn làm là để cho các nhà xây dựng chỉ khởi tạo trạng thái của các đối tượng và không phụ thuộc (chẳng hạn như bối cảnh dựng hình OpenGL). Tôi giả sử rằng hàm khởi tạo của OpenGLContext khởi tạo bối cảnh OpenGL Rendering Context và đó là những gì tôi không làm.Thay vào đó tôi muốn tạo ra các phương pháp CreateRenderingContext cho lớp OpenGLContext để làm khởi và cũng để gọi 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(); 
} 
Các vấn đề liên quan