Подтвердить что ты не робот

Определение объекта без вызова его конструктора в С++

В С++ я хочу определить объект как член класса, подобного этому:

Object myObject;

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

class Program
{
public:
   Object myObject; //Should not try to call the constructor or do any initializing
   Program()
   {
      ...

      //Now call the constructor
      myObject = Object(...);
   }

}
4b9b3361

Ответ 1

Сохраните указатель на Object, а не фактический Object

следующим образом:

class Program
{
public:
   Object* myObject; // Will not try to call the constructor or do any initializing
   Program()
   {
      //Do initialization
      myObject = new Object(...);  // Initialised now
   }

}

Не забывайте delete его в деструкторе. Современный С++ помогает вам там, поскольку вы можете использовать auto_ptr shared_ptr, а не указатель необработанной памяти.

Ответ 2

Другие опубликовали решения с использованием raw-указателей, но умная указатель была бы лучшей идеей:

class MyClass {
  std::unique_ptr<Object> pObj;
  // use boost::scoped_ptr for older compilers; std::unique_ptr is a C++0x feature
public:
  MyClass() {
    // ...
    pObj.reset(new Object(...));
    pObj->foo();
  }
  // Don't need a destructor
};

Это позволяет избежать необходимости добавления деструктора и неявно запрещает копирование (если вы не написали свои собственные operator= и MyClass(const MyClass &).

Если вы хотите избежать выделения отдельной кучи, это можно сделать с помощью boost aligned_storage и размещения new. Непроверенные:

template<typename T>
class DelayedAlloc : boost::noncopyable {
  boost::aligned_storage<sizeof(T)> storage;
  bool valid;
public:
  T &get() { assert(valid); return *(T *)storage.address(); }
  const T &get() const { assert(valid); return *(const T *)storage.address(); }

  DelayedAlloc() { valid = false; }

  // Note: Variadic templates require C++0x support
  template<typename Args...>
  void construct(Args&&... args)
  {
    assert(!valid);
    new(storage.address()) T(std::forward<Args>(args)...);
    valid = true;
  }

  void destruct() {
    assert(valid);
    valid = false;
    get().~T();
  }

  ~DelayedAlloc() { if (valid) destruct(); }
};

class MyClass {
  DelayedAlloc<Object> obj;
public:
  MyClass() {
    // ...
    obj.construct(...);
    obj.get().foo();
  }
}

Или, если Object является скопируемым (или подвижным), вы можете использовать boost::optional:

class MyClass {
  boost::optional<Object> obj;
public:
  MyClass() {
    // ...
    obj = Object(...);
    obj->foo();
  }
};

Ответ 3

Если у вас есть доступ к boost, есть удобный объект, который предоставляется с именем boost::optional<> - это позволяет избежать необходимости динамического выделения, например

class foo
{
  foo()  // default std::string ctor is not called..
  {
    bar = boost::in_place<std::string>("foo"); // using in place construction (avoid temporary)
  }
private:
  boost::optional<std::string> bar;
};

Ответ 4

Вы также можете переписать свой код, чтобы использовать список инициализаторов конструктора, если вы можете переместить другую инициализацию в конструкторы:

class MyClass
  {
    MyObject myObject; // MyObject doesn't have a default constructor
  public:
    MyClass()
      : /* Make sure that any other initialization needed goes before myObject in other initializers*/
      , myObject(/*non-default parameters go here*/)
      {
      ...
      }
  };

Вам нужно знать, что после такого шаблона вы попадаете в путь, где вы выполняете большую работу в конструкторах, что, в свою очередь, приводит к необходимости обработки исключений и безопасности (как канонический способ возврата ошибки из конструктора - это исключение).

Ответ 5

Для этого вы можете использовать указатель (или интеллектуальный указатель). Если вы не используете интеллектуальный указатель, убедитесь, что ваша свободная память кода при удалении объекта. Если вы используете смарт-указатель, не беспокойтесь об этом.

class Program
{
public:
   Object * myObject;
   Program():
      myObject(new Object())
   {
   }
   ~Program()
   {
       delete myObject;
   }
   // WARNING: Create copy constructor and = operator to obey rule of three.
}