Init var без конструктора копирования - PullRequest
1 голос
/ 29 мая 2010

У меня есть некоторый класс ( Окно ) без конструктора копирования (это личное). Я не могу понять, как инициировать var этого класса в моем собственном классе:

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

Ошибка 1 : initializing argument 1 of ‘Window::Window(WindowHandle, const sf::WindowSettings&)’

Ошибка 2 : ‘NonCopyable& NonCopyable::operator=(const NonCopyable&)’ is private

Но это работает так:

Window obj(/* constructor params */);

Ответы [ 7 ]

6 голосов
/ 29 мая 2010

Использовать список инициализаторов :

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

Это касается и ссылок. Вы можете назначить любую переменную-член в списке инициализатора. Это работает только в конструкторе.

Если вы хотите, чтобы он работал вне конструктора, вам нужно использовать указатель:

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

Обязательно освободите obj, используя delete в вашем деконструкторе (и сделайте деконструктор виртуальным, если необходимо).

4 голосов
/ 29 мая 2010

Вашему MyClass необходим конструктор для инициализации obj члена.

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

Если вам нужна функция init(), а объект Window предоставляет свой собственный init() функция какого-то рода, вы можете сделать это:

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

Если класс Window не имеет ничего подобного функции init(), вы можете использовать кучу (не рекомендуется, если вам абсолютно не нужно):

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

Если класс Window объявляет конструктор частной копии и / или оператор назначения копирования, вы не можете назначить новый экземпляр Window для obj.

1 голос
/ 29 мая 2010

Полагаю, я собираюсь получить полную информацию об этом (прочитайте до конца, прежде чем приступить к полному срыву), но ... при условии, что конструктор окон никогда не выдаст:

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

Я, конечно, подчеркну требование не бросать конструктора, как будто он бросает, вы останетесь в очень грязной ситуации: деструктор MyClass вызовет деструктор Window независимо от того, является ли объект живым и ударил ногами или сломался из-за неудачной конструкции, и в последнем случае вы получаете неопределенное поведение.

Конечно, типичная вещь, таким образом, будет std::unique_ptr<Window>, но у нас есть препятствие динамического распределения, когда ситуация явно не требует этого ...

Так что вам лучше использовать библиотеку: Boost.Optional .

class MyClass
{
public:

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

Синтаксический вызов похож на указатель:

obj->foo();

Но единственным преимуществом является то, что вы получаете разрушение / строительство на месте с более безопасной семантикой. Уничтожить легко:

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

Для построения используйте TypedInPlaceFactory . И для назначения тоже ... которое, конечно, сначала очищает предыдущий экземпляр (если есть):

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

Основным преимуществом является то, что если во время конструирования теперь возникает какое-либо исключение, объект optional остается в состоянии unitialized, что является вполне жизнеспособным, и, таким образом, вам не нужно бояться неопределенного поведения.

Так что в основном это похоже на динамически размещаемый объект, принадлежащий интеллектуальному указателю ... за исключением того, что магия заключается в том, что объект не выделяется динамически, что гарантирует те же характеристики, что и у "нормального" объекта:)

Я должен добавить, что препятствие, не подлежащее копированию, является одной из причин создания InPlaceFactory.

1 голос
/ 29 мая 2010

Если ваш конструктор копирования является приватным, класс имеет конструктор копирования. Кажется, у вашего класса есть и копия, и копия, и операция присваивания как частная, что объясняет второе сообщение об ошибке. Первое сообщение об ошибке связано с классом WindowHandle, который вы не показали.

Чтобы сделать это более понятным, нам нужно было бы также увидеть класс Window - есть ли у него (например) конструктор по умолчанию?

0 голосов
/ 29 мая 2010

Инициализация членов класса должна выполняться в конструкторе класса, как в следующем примере:

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

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

MyClass::MyClass(/* constructor params */) : m_obj(/* constructor params */)
{
}
0 голосов
/ 29 мая 2010

Если Window не имеет конструктора копирования, вы не можете назначить ему другой объект класса Window. Вы можете инициализировать obj только из конструктора MyClass, используя список инициализаторов. Например:

class MyClass
{
   Window obj; // Hasn't copy constructor
   public:
      MyClass()
          : obj(/*constructor params*/)
      {
          /*...*/
      }
}
0 голосов
/ 29 мая 2010

Весь смысл не в том, чтобы позволить вам клонировать его.

Инициализировать так: Окно obj (параметры другого конструктора, а не копии)

или

Window & obj = somefunctionConstructingSuchAWindow ();

...