web-dev-qa-db-ja.com

コンストラクターでメソッドを呼び出す

Herb Sutterは、彼の http://www.gotw.ca 記事の1つで、コンストラクターが完全に実行された場合にのみオブジェクトが構築されている(存在が有効である)と述べています。最後のブレースを超えます。

次のコードを考えてみましょう

class A
{
  public:
  A() 
  { 
      f();
  }

  void f() 
  { 
      cout << "hello, world"; 
  }

}; 

int main()
{
   A a;
}

ハーブが言っていることから、Aはコンストラクタ内で完全に構築されていないので、「this」ptrがまだ準備されていないため、コンストラクタ内での呼び出しf()は無効です。

それでも、コンストラクタ内には確かに有効な「this」があり、f()が呼び出されます。

私はハーブが何か間違ったことを言っているとは思いません...しかし、私はそれを間違って解釈していると思います。

ここに記事へのリンクがあります: http://www.gotw.ca/gotw/066.htm コンストラクターからの例外について話します。具体的にはここに私の質問の根拠となるそれからの抜粋があります:

-オブジェクトの寿命はいつ始まりますか? そのコンストラクタが正常に完了して正常に戻るとき。つまり、制御はコンストラクター本体の最後または以前のreturnステートメントに到達します。

-オブジェクトのライフタイムはいつ終了しますか?そのデストラクタが始まるとき。つまり、制御はデストラクタ本体の先頭に到達します。 ここでの重要な点は、存続期間が始まる前のオブジェクトの状態は、存続期間が終了した後の状態とまったく同じであるということです-オブジェクトがない、period。この観察は、私たちに重要な質問をもたらします:

C++コンストラクターモデルを次のように要約します。

Either:

(a) The constructor returns normally by reaching its end or a return statement, and the object exists.

Or:

(b) The constructor exits by emitting an exception, and the object not only does not now exist, but never existed.
22
Arun

ハーブが言っていることから、Aはコンストラクタ内で完全に構築されていないので、「this」ptrがまだ準備されていないため、コンストラクタ内での呼び出しf()は無効です。

これは、f()が_class A_またはその継承階層のvirtualメソッドであり、正しいオブジェクトに従ってf()の実行時の解決が予想される場合のみです。簡単に言うと、コンストラクター内でメソッドが呼び出されても、virtualメカニズムは作動しません。

f()が仮想関数ではない場合、f()の動作を正確に理解していれば、コンストラクターからそれを呼び出しても害はありません。プログラマは通常、コンストラクタからinitialize()のようなクラスメソッドを呼び出します。

ハーブサッターの記事へのリンクを教えていただけますか?

20
Nawaz

プログラムフローがコンストラクタに入るときまでに、オブジェクトのメモリが割り当てられ、thisポインタは実際に有効です。

ハーブの意味は、オブジェクトの状態が完全に初期化されていない可能性があるということです。特に、Aから派生したクラスを構築している場合、Aのコンストラクター内にいる間は、そのクラスのコンストラクターは呼び出されません。

Aのコンストラクター内から呼び出された場合、派生クラスの仮想関数は実行されないため、仮想メンバー関数がある場合、これは重要です。

8
Daniel Gehriger

注:正確な記事の方が簡単だったので、状況を把握できるようにしました

寿命に関する考慮事項は実際にはかなり複雑です。

オブジェクトのコンストラクターを考えると、2つの異なる観点があります。

  • 外部:つまり、オブジェクトのユーザー
  • 内部:つまり、コンストラクタとデストラクタを(特に)作成するとき

外部の観点から、オブジェクトの寿命:

  • コンストラクタが正常に完了したら開始します
  • デストラクタが実行を開始すると終了します

つまり、構築中または破壊中のオブジェクトにアクセスしようとすると、Bad Things Happen(tm)となります。これは主にマルチスレッドプログラムに関連していますが、オブジェクトへのポインターを基本クラスに渡すと発生する可能性があります...

...内部の視点。より複雑です。確かなことの1つは、必要なメモリが割り当てられていることですが、オブジェクトの一部がまだ完全に初期化されていない可能性があります(結局、それを構築しています)。

  • コンストラクターの本体では、クラスの属性とベースを使用でき(初期化されています)、関数を通常どおり呼び出します(仮想呼び出しは避けてください)。
  • 基本クラスの場合、派生オブジェクトはまだ初期化されていません(したがって、仮想呼び出しの制限)
3
Matthieu M.

ライフタイムがまだ開始されていないことによる影響は、主に、コンストラクターが例外をスローした場合、デストラクターが実行されないことです。

2
Simon Richter

まだ初期化されていないメンバー変数に注意してください。仮想関数に注意してください。関数が仮想であり、派生オブジェクトが作成されている場合、呼び出す関数は期待したものとは異なる場合があります。それ以外は、コンストラクターからのメソッドの呼び出しに問題はありません。特に、オブジェクトのメモリはすでに割り当てられています。

0
Oswald