6

I'm trying to learn SDL using C++. I've created a window.h header and a window.cpp source file for storing a Window class. In window.h it looks something like this:

Class Window {
public:
  Window();
    . . .
private:
  std::unique_ptr<SDL_Window, void (*)(SDL_Window*)> window;
  std::unique_ptr<SDL_Renderer, void (*)(SDL_Renderer*)> renderer;
    . . .
}

with some of the code in the class omitted. Then, in my source file, in the definition of the default constructor, I do this:

Window::Window() {
  window = std::unique_ptr<SDL_Window, void (*)(SDL_Window*)>(nullptr, SDL_DestroyWindow);
  renderer = std::unique_ptr<SDL_Renderer, void (*)(SDL_Renderer*)>(nullptr, SDL_DestroyRenderer);
}

However, when I go to compile, I'm told that unique_ptr [is] constructed with null function pointer deleter, which as far as I can tell is false. Maybe I'm misunderstanding how to use a unique_ptr's deleter, but I cannot figure out what's wrong. Is there something I'm missing or have I completely misunderstood what I'm doing?

abertsch
  • 103
  • 2
  • 8

2 Answers2

7

The problem is that in your constructor, you use assignment instead of initialization for the members window and renderer. Your members are implicitly default initialized, which generates an error.

But why is this so and how to change it?

If you're new to C++ this might sound a bit strange, but any class member is initialized before the constructor function body is evaluated. Per default, each member will be initialized with its default constructor, or left uninitialized (if it's a primitive type like int). If you want to change this behavior (i.e. if you want to initialize it with something different, like you want), you have to use the member initializer list.

Example:

Window::Window() :   // <-- put a colon here
    windows(nullptr, SDL_DestroyWindow),     // here comes the member init list
    rendered(nullptr, SDL_DestroyRenderer)
{
}

Note: Since C++11, you can also initialize members at their definition directly (like you can do it in Java for example), but in your case this would make the header look too complex. Also, this wouldn't fit to the rule of encapsulation (in most cases, only the class implementation should know what to do with private members).

leemes
  • 44,967
  • 21
  • 135
  • 183
  • Thank you, that makes perfect sense! I guess I've been spoiled by Java, which is probably a phrase you never hear. After using an initializer list, everything is working. Do you mind explaining why all members are initialized before the constructor? – abertsch Apr 07 '14 at 14:54
  • Well they have to be initialized somewhere. And C++ is designed such that as soon as the body of your constructor is run, this step already took place. Can't explain it any better, sorry :) But if you're interested, the wiki links I posted above explain it better. – leemes Apr 07 '14 at 15:19
1

Use a member initializer list:

Window::Window()
    : windows(nullptr, SDL_DestroyWindow), rendered(nullptr, SDL_DestroyRenderer)
{
    // empty
}

By the time the body of a constructor runs, all members shall already be initialized (default constructed unless you explicitly do something else, like above). Thereafter, you can only assign to them.

jrok
  • 54,456
  • 9
  • 109
  • 141