これは安全ですか?実際の実装では仮想関数を使用していませんが、使用したとしても安全であると信じたくなりました。
class Foo
{
Foo()
{
// initialize things
}
Foo( int )
{
new ( this ) Foo();
}
}
これは安全ですか?実際の実装では仮想関数を使用していませんが、使用したとしても安全であると信じたくなりました。
class Foo
{
Foo()
{
// initialize things
}
Foo( int )
{
new ( this ) Foo();
}
}
コンストラクターの開いた中括弧を入力するまでにFoo(int)
、すべてのクラスメンバーのコンストラクターが呼び出されています。次に、配置がnewの別のコンストラクターを強制的に呼び出すと、クラスの現在の状態が上書きされます。これは基本的に、すべてのメンバーがコンストラクターを2回呼び出すことを意味します。コンストラクターで何かがnew
行われると、そのコンテンツがリークされ、本当に本当に混乱します。 2つのオブジェクトを効果的に構築しており、2番目のオブジェクトが最初のオブジェクトのメモリを上書きするため、最初のオブジェクトのメンバーのデストラクタが呼び出されることはありません。
言い換えれば、それは悪いです!やらないで!
最も一般的な回避策は、ある種の初期化関数を使用し、それを両方のコンストラクターから呼び出すことです。ただし、これでは、初期化子リストに含まれている必要があるconstメンバーやその他のメンバーを初期化することはできません。
私が心配しているのは、Fooが多重継承を使用している場合、this
最初に最も基本的なクラスへのポインターをキャストする必要があるということです。そうでなければ、this
がオフセットされている場合(多重継承で発生する場合があります)、間違ったアドレスで構築されます。
たとえば、別のクラスを拡張し、そのクラスにデストラクタがあった場合、安全ではありません。
class Foo
{
int* a;
public:
Foo():a(new int)
{
}
~Foo(){delete a;}
}
class Bar:public Foo
{
Bar()
{
// initialize things
}
Bar( int )
{
new ( this ) Foo();
}
}
最初にBar(int)
を呼び出しFoo()
、次に、をBar()
呼び出しますFoo()
。2回目Foo()
は、への最初の呼び出しで設定されたポインタを上書きしFoo()
、割り当てられたメモリがリークされます。
ここでの重要な問題は、コンストラクターが特別であるということです。コンストラクターを呼び出すコンストラクターを作成する場合(たとえば、new
キーワードを使用してオブジェクトを作成する場合)、コンストラクター本体が実行されるだけでなく、オブジェクトのチェーン全体が最初に構築されます。
したがって、配置構文を使用して別のコンストラクターを最初に実行すると、C ++はすべての基本クラスオブジェクトコンストラクターとすべてのメンバー変数コンストラクターを自動的に再実行し、次に他のコンストラクター本体が呼び出されます。大丈夫な場合もありますが、多くの場合、予期しない動作に遭遇します。
この問題の最善の解決策は、初期化を行う別の関数を作成することです。
class Foo
{
inline void nullify()
{
// initialize things
}
Foo()
{
nullify();
}
Foo( int )
{
nullify();
}
}