it-swarm.com.de

Definieren eines Objekts ohne Aufruf seines Konstruktors in C++

In C++ möchte ich ein Objekt als Member einer Klasse wie folgt definieren:

Object myObject;

Dadurch wird jedoch versucht, seinen parameterlosen Konstruktor aufzurufen, der nicht vorhanden ist. Allerdings muss der Konstruktor aufgerufen werden, nachdem die enthaltende Klasse einige Initialisierungen durchgeführt hat. Etwas wie das.

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

Speichern Sie einen Zeiger auf eine Object statt auf eine tatsächliche Object.

somit:

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

}

Vergessen Sie nicht, delete es im Destruktor zu machen. Modernes C++ hilft Ihnen dabei, indem Sie ein auto_ptr shared_ptr anstelle eines unformatierten Speicherzeigers.

22
Julian

Andere haben Lösungen veröffentlicht, die rohe Zeiger verwenden, aber ein intelligenter Zeiger wäre eine bessere Idee:

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

Dies vermeidet die Notwendigkeit, einen Destruktor hinzuzufügen, und verbietet implizit das Kopieren (es sei denn, Sie schreiben Ihren eigenen operator= und MyClass(const MyClass &).

Wenn Sie eine separate Heap-Zuordnung vermeiden möchten, können Sie dies mit dem aligned_storage von boost und der Platzierung new tun. Ungetestet:

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

Wenn Object kopierbar (oder verschiebbar) ist, können Sie boost::optional verwenden:

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

Wenn Sie Zugriff auf Boost haben, gibt es ein praktisches Objekt mit dem Namen boost::optional<> - dies vermeidet die Notwendigkeit einer dynamischen Zuweisung, z.

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

Möglicherweise können Sie Ihren Code auch für die Verwendung der Konstruktor-Initialisierungsliste neu schreiben, wenn Sie die andere Initialisierung in Konstruktoren verschieben können:

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

Sie müssen sich dessen bewusst sein, dass das Befolgen eines solchen Musters zu einem Pfad führt, auf dem Sie viel mit Konstruktoren arbeiten, was wiederum dazu führt, dass Sie Ausnahmebehandlung und -sicherheit (als kanonische Methode, um einen Fehler von einem Konstruktor zurückgeben zu können) benötigen ist eine Ausnahme zu werfen).

4

Sie können dazu einen Zeiger (oder einen intelligenten Zeiger) verwenden. Wenn Sie keinen intelligenten Zeiger verwenden, stellen Sie sicher, dass der Code frei ist, wenn das Objekt gelöscht wird. Wenn Sie einen intelligenten Zeiger verwenden, machen Sie sich keine Sorgen.

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

Sie können die Objektkonstruktion und -zerstörung durch diesen Trick vollständig steuern:

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

Bewerben Sie sich auf Ihre Probe:

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

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

}

Der große Vorteil dieser Lösung besteht darin, dass keine zusätzlichen Zuordnungen erforderlich sind und der Objektspeicher normal zugewiesen wird. Sie haben jedoch die Möglichkeit, den Konstruktor aufzurufen.

Ein weiterer Beispiellink

0
Evgeny Mamontov