2016-10-14 7 views
2

メソッドチェインを使用するシンプルなC++プログラムがあり、デストラクタは連鎖呼び出しでのみ使用されると2回呼び出されることに気付きました。これはチェーンコールにコンストラクタも含まれている場合にのみ発生します。個別に呼び出された場合、デストラクタは一度だけ呼び出されます。C++メソッド連鎖によりデストラクタが2回呼び出される

コードは以下の通りです:このアプリケーションの

class Foo { 
public: 
    Foo() { cout << "-- constructor " << this << endl; } 
    ~Foo() { cout << "-- destructor " << this << endl; }; 

    Foo& bar() { 
     cout << "---- bar call " << this << endl; 
     return *this; 
    } 
}; 

int main() { 
    cout << "starting test 1" << endl; 
    { 
     Foo f = Foo(); 
    } 
    cout << "ending test 1" << endl << endl; 
    cout << "starting test 2" << endl; 
    { 
     Foo f = Foo().bar(); 
    } 
    cout << "ending test 2" << endl; 
    return 0; 
} 

結果は以下の通りです:

starting test 1 
-- constructor 0x7ffd008e005f 
-- destructor 0x7ffd008e005f 
ending test 1 

starting test 2 
-- constructor 0x7ffd008e005f 
---- bar call 0x7ffd008e005f 
-- destructor 0x7ffd008e005f 
-- destructor 0x7ffd008e005f 
ending test 2 

は、この標準の動作(?もしそうなら、なぜそれがある)か、それとも私はいくつかのミスをしたのですか?これを防ぐことはできますか?

+1

は1 –

+1

がすべての答えで述べたコピーは以下で何が起こっているかを確認するためにコピーコンストラクタと代入演算子を追加します[ elided](http://en.cppreference.com/w/cpp/language/copy_elision)を参照してください。 'g ++ -fno-elide-constructors'を使ってコンパイルすると、2つのデストラクタが呼び出されます。 – vsoftco

+0

それはかなり面白いです。ありがとう! – x3mspeedy

答えて

6

Foo f = Foo().bar();は、現在、コンパイラが生成するものですので、コンソールへの出力は何もないコピーコンストラクタFooを呼び出します。 これはだから、コンストラクタよりも多くのデストラクタを呼び出すように見えます。

コピーを取り除くためにconst Foo& f = Foo().bar();と書くことができます。 constを使用すると、匿名の一時的なライフタイムも長くなります。行に関与するタイプFooの二つのオブジェクトが存在すること

5

​​

しかし、一方はコンストラクタとコピーコンストラクタを介して他の一つを介して作成されます。それで、建設のために1行しか印刷されず、破壊のために2行しか印刷されないという理由があります。コードはすべて問題なく、コピーコンストラクターを実装して一貫した出力を確認するだけで済みます。

3

これは予期された動作です。 barから参照を返しても、あなたはそれを使用していません。

は、値で戻り値を取得してコピーを作成します。つまり、FooからFoo()が式の最後に破棄され、スコープを終了するとfにコピーされたコピーが破棄されます。

5

自分で実装していない別のコンストラクタがあります。コピーコンストラクタ。

あなたのコールFoo f = Foo().bar();のみごtmpオブジェクトのインスタンス化は、あなたのコンストラクタを呼び出しますFoo tmp = Foo(); Foo f = tmp.bar();

のように書くことができます。 fのコンストラクタは自動的に生成されたコピーコンストラクタです。

これはあなたにいくつかのより良い出力与える必要があります:

#include <iostream> 
using std::cout; 
using std::endl; 

class Foo { 
public: 
    Foo() { cout << "-- constructor " << this << endl; } 
    Foo(const Foo& f) { cout << "-- copy-constructor " << this << endl; } 
    ~Foo() { cout << "-- destructor " << this << endl; }; 

    Foo& bar() { 
     cout << "---- bar call " << this << endl; 
     return *this; 
    } 
}; 

int main() { 
    cout << "starting test 1" << endl; 
    { 
     Foo f = Foo(); 
    } 
    cout << "ending test 1" << endl << endl; 
    cout << "starting test 2" << endl; 
    { 
     Foo f = Foo().bar(); 
    } 
    cout << "ending test 2" << endl; 
    return 0; 
} 

-

starting test 1 
-- constructor 000000EC09CFF944 
-- destructor 000000EC09CFF944 
ending test 1 

starting test 2 
-- constructor 000000EC09CFFA44 
---- bar call 000000EC09CFFA44 
-- copy-constructor 000000EC09CFF964 
-- destructor 000000EC09CFFA44 
-- destructor 000000EC09CFF964 
ending test 2 
関連する問題