Определение объекта без вызова его конструктора в C ++ - PullRequest
44 голосов
/ 26 сентября 2011

В C ++ я хочу определить объект как член класса следующим образом:

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(...);
   }

}

Ответы [ 6 ]

23 голосов
/ 26 сентября 2011

Сохраните указатель на 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 указать его в деструкторе.В этом вам помогает современный C ++, в котором вы можете использовать auto_ptr shared_ptr вместо необработанного указателя памяти.

15 голосов
/ 26 сентября 2011

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

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

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

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

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();
  }
};
5 голосов
/ 26 сентября 2011

Если у вас есть доступ к 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 голосов
/ 19 сентября 2012

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

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*/)
      {
      ...
      }
  };

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

1 голос
/ 05 апреля 2018

Вы можете полностью контролировать строительство и разрушение объекта с помощью этого трюка:

template<typename T>
struct DefferedObject
{
    DefferedObject(){}
    ~DefferedObject(){ value.~T(); }
    template<typename...TArgs>
    void Construct(TArgs&&...args)
    {
        new (&value) T(std::forward<TArgs>(args)...);
    }
public:
    union
    {
        T value;
    };
};

Примените к вашему образцу:

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

      //Now call the constructor
      myObject.Construct(....);
   }

}

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

Другой пример ссылки

0 голосов
/ 26 сентября 2011

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

class Program
{
public:
   Object * myObject;
   Program():
      myObject(new Object())
   {
   }
   ~Program()
   {
       delete myObject;
   }
   // WARNING: Create copy constructor and = operator to obey rule of three.
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...