web-dev-qa-db-fra.com

Définir un objet sans appeler son constructeur en C++

En C++, je veux définir un objet en tant que membre d'une classe comme ceci:

Object myObject;

Cependant, cela tentera d’appeler son constructeur sans paramètre, qui n’existe pas. Cependant, j'ai besoin que le constructeur soit appelé après que la classe contenante ait initialisé. Quelque chose comme ça.

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

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

}
41
Hannesh

Stocker un pointeur sur une Object plutôt qu'une réelle Object

ainsi:

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

}

N'oubliez pas de delete dans le destructeur. Le C++ moderne vous y aide, en ce sens que vous pouvez utiliser un auto_ptr shared_ptr plutôt qu'un pointeur de mémoire brute.

22
Julian

D'autres ont publié des solutions en utilisant des pointeurs bruts, mais un pointeur intelligent serait une meilleure idée:

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

Cela évite d'ajouter un destructeur et interdit implicitement la copie (sauf si vous écrivez vos propres operator= et MyClass(const MyClass &).

Si vous souhaitez éviter une allocation distincte de segment de mémoire, vous pouvez le faire avec aligned_storage de boost et le placement new. Non testé:

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

Ou, si Object est copiable (ou déplaçable), vous pouvez utiliser boost::optional:

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

Si vous avez accès à Boost, un objet très pratique, appelé boost::optional<>, est fourni. Cela évite le recours à une allocation dynamique, par exemple.

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

Vous pourrez peut-être également réécrire votre code pour utiliser la liste d'initialisation du constructeur, si vous pouvez déplacer l'autre initialisation en constructeurs:

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

Vous devez savoir que suivre un tel modèle vous mènera à un chemin dans lequel vous effectuerez beaucoup de travail dans les constructeurs, ce qui vous obligera à saisir la gestion des exceptions et la sécurité (en tant que moyen canonique de renvoyer une erreur d'un constructeur est de jeter une exception).

4
Joris Timmermans

Vous pouvez utiliser un pointeur (ou un pointeur intelligent) pour le faire. Si vous n'utilisez pas de pointeur intelligent, assurez-vous que votre code libère de la mémoire lorsque l'objet est supprimé. Si vous utilisez un pointeur intelligent, ne vous inquiétez pas.

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

Vous pouvez totalement contrôler la construction et la destruction de l'objet par cette astuce:

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

Appliquez sur votre échantillon:

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

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

}

Le gros avantage de cette solution est qu’elle ne nécessite aucune allocation supplémentaire ni mémoire d’objet allouée normalement, mais vous avez le contrôle lorsque vous appelez le constructeur.

Un autre exemple de lien

0
Evgeny Mamontov