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(...);
}
}
実際の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。
他の人は生のポインターを使用してソリューションを投稿しましたが、スマートポインターの方が良いでしょう:
_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();
}
};
_
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;
};
他の初期化からコンストラクターに移動できる場合は、コンストラクター初期化リストを使用するようにコードを書き換えることもできます。
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*/)
{
...
}
};
そのようなパターンに従うと、コンストラクターで多くの作業を行うパスにつながることに注意する必要があります。これにより、例外処理と安全性を把握する必要が生じます(コンストラクターからエラーを返す標準的な方法として)例外をスローすることです)。
このトリックにより、オブジェクトの構築と破棄を完全に制御できます。
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(....);
}
}
このソリューションの大きな利点は、追加の割り当てが不要であり、オブジェクトメモリが通常どおり割り当てられることですが、コンストラクターを呼び出すときに制御できます。
これを行うには、ポインター(またはスマートポインター)を使用できます。スマートポインターを使用しない場合は、オブジェクトが削除されたときにコードがメモリを解放することを確認してください。スマートポインターを使用する場合、心配する必要はありません。
class Program
{
public:
Object * myObject;
Program():
myObject(new Object())
{
}
~Program()
{
delete myObject;
}
// WARNING: Create copy constructor and = operator to obey rule of three.
}