Sign in to follow this  
SteveHatcher

When to use pointers or objects in 'Composition'?

Recommended Posts

Before I start please know I am a beginner so I may be asking a silly question or getting terminology wrong.

 

I have a simple program which initializes a win32 window, and then initializes directX 11 in that window.

 

The directX initialization is held in a class Graphics

 

The win32 window is held in a class MainWindow. My program flow goes as follows:

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    MSG msg;
    window = new MainWindow;
    window->initialize(hwnd, hInstance, nCmdShow);

the MainWindow::initialize function does all of that registering a win32 class and creating the window. At the bottom of this function is where I want to initialize directX within the window. This is where I get confused. As far as I am aware there seems to be three ways to do this.

 

METHOD 1. Use inheritance.

class MainWindow : public Graphics
{
private:
    HWND hwnd;
    HINSTANCE hInstance;
    HRESULT hr;
    int nCmdShow;

public:
    MainWindow();
    ~MainWindow();
    void initialize(HWND hwnd, HINSTANCE hInstance, int nCmdShow);
    static LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);
};

then after the ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); of my MainWindow::initialize() function, I can write.

    Graphics graphics;
    
    graphics.initialize(hwnd, 640, 480, false);
    graphics.showBackbuffer();

METHOD 2. Composition with an object.

Removing the inheritance line : public Graphics, means I need to put a Graphics object in my MainWindow member variable section e.g.

class MainWindow
{
private:
    HWND hwnd;
    HINSTANCE hInstance;
    Graphics graphics; <------------- ADDED THIS
    HRESULT hr;
    int nCmdShow;

I can now leave the same code as before, but remove the Graphics graphics; object declaration since it has already been done.

 

METHOD 3. Composition with a pointer to a graphics object.

The third method is to declare a pointer to a graphics object (which will be created later), in class MainWindow e.g.

class MainWindow
{
private:
    HWND hwnd;
    HINSTANCE hInstance;
    Graphics *graphics; <------------- ADDED THIS
    HRESULT hr;
    int nCmdShow;

This time, to initialize my directX window I write

    graphics = new Graphics();
    graphics->initialize(hwnd, 640, 480, false);
    graphics->showBackbuffer();

At this stage I am very confused. They all essentially get me where I want to be, but there would be a lot of behind the scenes stuff going on of which I have no idea.

 

So could someone clarify which method is best to use in this example?

 

Thanks

 

 

 

Share this post


Link to post
Share on other sites

I would recommend separating responsabilities.

 

The graphics module doesn't need a reference to the window class, it only needs a window handle (on DirectX) or a handle to the device context (in OpenGL).

 

So, you pass a void* pointer to the graphics class, convert it to the desired handle, and initialize the graphics API. 

 

Example:

bool Graphics::InitApi( void* _pvPtr ) {
           #ifdef DIRECTX
                   return DirectX::Init( reinterpret_cast<HWND>(_pvPtr) );   
           #elseif OPENGL
                   return OpenGl::Init( reinterpret_cast<HDC>(_pvPtr) );    
}

The advantage is that your graphics module it's abstracted and less dependent of your current platform. Just make sure to destroy the API right after the window is destroyed.

Edited by Irlan

Share this post


Link to post
Share on other sites

I would recommend separating responsabilities.

 

The graphics module doesn't need a reference to the window class, it only needs a window handle (on DirectX) or a handle to the device context (in OpenGL).

 

So, you pass a void* pointer to the graphics class, convert it to the desired handle, and initialize the graphics API. 

 

Example:

bool Graphics::InitApi( void* _pvPtr ) {
           #ifdef DIRECTX
                   return DirectX::Init( reinterpret_cast<HWND>(_pvPtr) );   
           #elseif OPENGL
                   return OpenGl::Init( reinterpret_cast<HDC>(_pvPtr) );    
}

The advantage is that your graphics module it's abstracted and less dependent of your current platform. Just make sure to destroy the API right after the window is destroyed.

And the nicer way of achieving this is abstracting the HDC and DX device away in an interface that has platform specific implementations. This way you dont have to deal with the defines in systems that dont need to be aware of those defines.

Share this post


Link to post
Share on other sites


And the nicer way of achieving this is abstracting the HDC and DX device away in an interface that has platform specific implementations. This way you dont have to deal with the defines in systems that dont need to be aware of those defines.

 

This is the responsability of the DirectX and OpenGl classes.

Share this post


Link to post
Share on other sites

In all my stuff, my main Win32 window class and my main graphics object are actually completely unrelated. I just give the graphics constructor the HWND it needs.

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    Window window(...);
    Graphics graphics(window.getHandle(), ...);
    while (window.processMessages())
    {
        ...
    }
}

I then have higher level logic that contains both these components to deal with both together (e.g. the game loop, managing window/fullscreen/borderless transitions, resizing, etc.).

Share this post


Link to post
Share on other sites

I prefer method 2 for composition, although I sometimes use method 3 for the following reasons.

1. using pointers, you can forward declare class Graphics in the header to reduce dependencies, which in turn

reduces compile times when you modify class Graphics

2. using pointers, you can put all the initialize() work in the constructor and you don't have to worry about

your object being in invalid state. (in cases where you can't use initializer lists)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this