web-dev-qa-db-ja.com

初期化リストで「this」ポインタを使用しても安全ですか?

親子関係を持つ2つのクラス(Parentクラスは "has-a" Childクラス)があり、ChildクラスにはParentへのポインターがあります。次のように、子の構築時に親ポインタを初期化すると便利です。

class Child;
class Parent;

class Child
{
public:
 Child (Parent* parent_ptr_) : parent_ptr(parent_ptr_) {};

private:
 Parent* parent_ptr;
};

class Parent
{
public:
    Parent() : child(this) {};

private:
    Child child;
}

今、私は人々が初期化リストでthisを使用しないことを推奨していることを知っています、そして C++ FAQ はコンパイラ警告を受け取るつもりです(BTW、VS2010では警告を受けません)が、私は本当にこのようにすると、Parentのコンストラクターでいくつかのセット関数を呼び出すことができます。私の質問は:

  • thisオブジェクトが作成されるときに、親Childポインターは明確に定義されていますか?
  • もしそうなら、なぜそれを上記のように使用することが悪い習慣と考えられているのですか?

おかげで、

ボアズ

編集:Timboに感謝します。それは確かに 重複 です(ええと、私は同じクラス名を選択しさえしました)。それでは、いくつかの付加価値を得ましょう:参照はどうですか?以下を行うことは可能/安全ですか? :

class Child
{
public:
 Child (Parent& parnet_ptr_) : parent_ptr(parent_ptr_) {};

private:
 Parent* parent_ptr;
};

class Parent
{
public:
    Parent() : child(*this) {};

private:
    Child child;
}
52
bavaza

はい。 thisポインターを初期化リストで使用しても安全です。初期化されていないメンバーまたは仮想関数に直接または間接的にアクセスするために使用されていない限り )、オブジェクトがまだ完全に構築されていないため。オブジェクトchildは、後で使用するためにthisParentポインターを格納できます!

48
Nawaz

thisポインター(「ポインター用語」)isは明確に定義されています(そうでない場合、親コンストラクターはどのインスタンスで動作しているかをどのようにして知るのでしょうか)。

  • Childオブジェクトの後に宣言されたフィールドはまだ初期化されていません。
  • コンストラクタのコードはまだ実行されていません。
  • また、コンストラクターからの仮想メンバーの使用に関する通常の警告が適用されます1

したがって、一般的に親オブジェクトはまだ一貫性のない状態です。子オブジェクトが親オブジェクトの構築で実行するすべてのこと、半分構築されたオブジェクトで実行されること、およびこれは一般に良いことではありません(たとえば、「通常の」メソッドを呼び出す場合-これは、オブジェクトは完全に構築されています-「不可能な」コードパスに入る可能性があります)。

それでも、子オブジェクトがそのコンストラクターで親ポインターを使用してすべてそれを保存して後で使用できるようにする場合(=>実際に構築されるとき)、問題はありません。


  1. つまり、仮想ディスパッチはコンストラクターでは機能しません。これは、vtableが派生クラスコンストラクターによってまだ更新されていないためです。たとえば、 ここ
11
Matteo Italia

afterParentオブジェクトが完全に構築されるまでポインタを逆参照しない限り、動作は明確に定義されています(@Sergeyが以下のコメントで言っているように、構築されるオブジェクトは実際にはParentから派生しているため、そのコンストラクタのallが完了している必要があります)。

4