views:

110

answers:

7

Hello, I have some class(Window) without copy constructor (it's private). I can't understand how to init var of this class in my own class:

class MyClass
{
   Window obj; // Hasn't copy constructor
   public:
      void init()
      {
         obj = Window(/* constructor params */); // [error]
         obj(/* constructor params */); // [error]
      }
}

Error 1: initializing argument 1 of ‘Window::Window(WindowHandle, const sf::WindowSettings&)’

Error 2: ‘NonCopyable& NonCopyable::operator=(const NonCopyable&)’ is private

But it works in this way:

Window obj(/* constructor params */);
+5  A: 

Use an initializer list:

class MyClass
{
   Window obj; // Hasn't copy constructor
   public:
      MyClass() :
         obj(/* constructor params */)
      {
      }
}

This goes for references, too. You can assign any member variable in an initializer list. It only works in the constructor, though.

If you want it to work outside a constructor, you need to use a pointer:

class MyClass
{
   Window *obj;
   public:
      void init()
      {
         obj = new Window(/* constructor params */);
      }
}

Be sure to deallocate obj using delete in your deconstructor (and make the deconstructor virtual if necessary).

strager
Only constructors can have initializer lists.
In silico
@In silico, True; I overlooked that. Sorry.
strager
If you are going to use the heap, make sure `obj` is managed properly. See my answer for how this can be done.
In silico
@In silico, D'oh, forgot that. I haven't used C++ in a long time. xD
strager
+3  A: 

Your MyClass needs a constructor to initialize the obj member.

class MyClass 
{ 
private:
    Window obj;
public: 
    MyClass() : obj(/* constructor params */) // This is an initializer list
    {}
};

If you need the init() function, and the Window object provides its own init() function of some sort, you can do this:

class MyClass 
{ 
private:
    Window obj;
public: 
    void init() 
    { 
        obj.init(/* init params */); // Window's own init() function
    }
};

If the Window class does not have anything like an init() function, you can use the heap (not recommended unless you absolutely have to):

class MyClass 
{ 
private:
    // Alternatively, we can use a smart pointer here and avoid
    // managing memory ourselves.
    Window* obj;
public: 
    MyClass() : obj(0) {}
    ~MyClass() { uninit(); }
    void init() 
    { 
        uninit();
        obj = new Window(/* constructor params */);
    }
    void uninit()
    {
        if(obj != 0)
        {
            delete obj;
            obj = 0;
        } 
    }
};

If the Window class declares a private copy constructor and/or copy assignment operator, then you cannot assign a new Window instance to obj.

In silico
Deleting a null pointer is [safe](http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.8), so you don't need the `if(obj != 0)` check in the uninit function.
Firas Assaad
Calling delete on a NULL pointer is safe if and only if it is the default global delete and that global delete is not buggy. I write portable code that needs to be robust under varying build and execution environments so I always check for NULL before delete.
Amardeep
The standard explicitly states that `delete` should behaves correctly when called with a null pointer. I would stray away from envs where it isn't the case, if they failed such a simple requirement I am very afraid of what else surprises I might encounter.
Matthieu M.
Also, heap allocation is not necessary (though the first reflex I guess), check out my answer for a detailed work around.
Matthieu M.
A: 

The whole point is not letting you clone it.

Initialize like this: Window obj(parameters of other constructor, not the copy one)

or

Window &obj = somefunctionConstructingSuchAWindow();

Pavel Radzivilovsky
+1  A: 

If your copy constructor is private the class does have a copy constructor. It seems your class has bothy copy ctor and assignment op as private, which explains the second error message. The first error message has something to do with the WindowHandle class, which you haven't shown.

To make much more sense of this, we'd need to see the Window class too - does it (for example) have a default constructor?

anon
http://www.sfml-dev.org/documentation/1.6/classsf_1_1Window.htm#49db47a79ca98b7d65087adeea06e919
Ockonal
@Neil: there is no need for `Window` to be default constructible. The only requirement we need is for its destructor not to throw (which is of course only good sense). After that there are memory alignement issues etc... but then, we have libraries for that ;)
Matthieu M.
A: 

If Window doesn't have a copy constructor, you cannot assign another object of class Window to it. You can initialize obj only from the constructor of MyClass using an initializer list. For example:

class MyClass
{
   Window obj; // Hasn't copy constructor
   public:
      MyClass()
          : obj(/*constructor params*/)
      {
          /*...*/
      }
}
Michał Trybus
A: 

The initialization of class members should be done on class constructor like on the following example:

class MyClass
{
public:
   MyClass(/* constructor params */);

private:
   Window m_obj; // Hasn't copy constructor
};

MyClass::MyClass(/* constructor params */) : m_obj(/* constructor params */)
{
}
Jorg B Jorge
A: 

I suppose I'm going to get thoroughly trashed for that (read till the end before going all fuming) but... assuming the constructor of windows never throws:

void MyClass::init()
{
  obj::~Window();          // destroy the object
  new (&obj) Window(...);  // construct the object
};

I shall of course underline the not throw requirement of the constructor as if it throws you'll be left with a very muddy situation: the destructor of MyClass will call the destructor of Window regardless of whether or not the object is alive and kicking or trashed because of a failed construction, and in the latter case you get undefined behavior.

Of course, a typical thing will thus be std::unique_ptr<Window> but we have the hurdle of dynamic allocation where clearly the situation does not mandate it...

So you'd be better off using a library: Boost.Optional.

class MyClass
{
public:

private:
  boost::optional<Window> obj;
};

The syntax invocation is similar to a pointer:

obj->foo();

But the one benefit is that you get inplace destruction / construction with safer semantics. Destruction is easy:

// both call ~Window() if an instance had been constructed
obj.reset();
obj = detail::none_t();

For construction use a TypedInPlaceFactory. And for assignment too... which of course clears up the previous instance (if any) first:

void MyClass::init(Arg1 arg1, Arg2 arg2)
{
  obj = boost::in_place<Window>(arg1, arg2);
}

The main advantage is that if any exception is now encountered during construction, the optional object is left in the unitialized state, which is perfectly viable, and thus you need not fear undefined behavior.

So basically it's like having a dynamically allocated object owned by a smart pointer... except that the magic is that the object is not dynamically allocated, thus guaranteeing the very same performances that a "normal" object :)

I should add that the non-copyable hurdle is one of the reason behind the InPlaceFactory creation.

Matthieu M.