web-dev-qa-db-ja.com

C ++でコンストラクターを呼び出さずにオブジェクトを定義する

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

}
44
Hannesh

実際の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。

23
Julian

他の人は生のポインターを使用してソリューションを投稿しましたが、スマートポインターの方が良いでしょう:

_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 &)を記述しない限り。

個別のヒープ割り当てを回避したい場合は、ブーストの_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();
  }
};
_
15
bdonlan

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

他の初期化からコンストラクターに移動できる場合は、コンストラクター初期化リストを使用するようにコードを書き換えることもできます。

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

そのようなパターンに従うと、コンストラクターで多くの作業を行うパスにつながることに注意する必要があります。これにより、例外処理と安全性を把握する必要が生じます(コンストラクターからエラーを返す標準的な方法として)例外をスローすることです)。

4

このトリックにより、オブジェクトの構築と破棄を完全に制御できます。

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

}

このソリューションの大きな利点は、追加の割り当てが不要であり、オブジェクトメモリが通常どおり割り当てられることですが、コンストラクターを呼び出すときに制御できます。

別のサンプルリンク

1
Evgeny Mamontov

これを行うには、ポインター(またはスマートポインター)を使用できます。スマートポインターを使用しない場合は、オブジェクトが削除されたときにコードがメモリを解放することを確認してください。スマートポインターを使用する場合、心配する必要はありません。

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