2

C++ でオブジェクトを作成および破棄する順序について詳しく学習するための簡単なプログラムを作成しました (Visual Studio 2015 を使用)。ここにあります:

#include <iostream>
#include <string>

using namespace std;

class A
{
public:
    A(string name)
        : name(name)
    {
        cout << "A(" << name << ")::constructor()" << endl;
    }
    ~A()
    {
        cout << "A(" << name << ")::destructor()" << endl;
    }
private:
    string name;
};

class C
{
public:
    C(string name, A a)
        : name(name), a(a)
    {
        cout << "C(" << name << ")::constructor()" << endl;
    }
    ~C()
    {
        cout << "C(" << name << ")::destructor()" << endl;
    }
private:
    string name;
    A a;
};

class B
{
public:
    B(string name)
        : name(name)
    {
        cout << "B(" << name << ")::constructor()" << endl;
    }
    ~B()
    {
        cout << "B(" << name << ")::destructor()" << endl;
    }
private:
    string name;
    A a1{"a1"};
    A a2{"a2"};
    C c1{"c1", a1};
    A a3{"a3"};
};

int main()
{
    B b("b1");
    return 0;
}

出力は私を少し驚かせました(a1s):

A(a1)::constructor()
A(a2)::constructor()
C(c1)::constructor()
A(a1)::destructor()
A(a3)::constructor()
B(b1)::constructor()
B(b1)::destructor()
A(a3)::destructor()
C(c1)::destructor()
A(a1)::destructor()
A(a2)::destructor()
A(a1)::destructor()

何が起こっているのかをもっと知るために、オブジェクトのインスタンスに関する情報を追加しました:

    A(string name)
        : name(name)
    {
        cout << "A(" << name << ")::constructor(), this = " << this << endl;
    }
    ~A()
    {
        cout << "A(" << name << ")::destructor(), this = " << this << endl;
    }

結果はさらに驚くべきものでした:

A(a1)::constructor(), this = 0039FB28
A(a2)::constructor(), this = 0039FB44
C(c1)::constructor()
A(a1)::destructor(), this = 0039F8A8
A(a3)::constructor(), this = 0039FB98
B(b1)::constructor()
B(b1)::destructor()
A(a3)::destructor(), this = 0039FB98
C(c1)::destructor()
A(a1)::destructor(), this = 0039FB7C
A(a2)::destructor(), this = 0039FB44
A(a1)::destructor(), this = 0039FB28

つまり、なぜa1のコンストラクターは1 回だけ呼び出され、デストラクタは3 回呼び出されるのでしょうか。私はa値渡しをしているので、明らかに少なくとも 1 つの一時オブジェクトが作成されますが、いついくつの Aインスタンスが作成および破棄されるのか説明してください。

4

1 に答える 1

5

コメントで既に述べたように、型のオブジェクトはA、値によって引数として渡すときに、コピー構築によっても構築されます。これを確認するために、自分でコピー コンストラクターを追加できます。

A(const A& other)
: name(other.name)
{
    cout << "A(" << name << ")::copy-constructor(), this = " << this << endl;
}

出力例:

A(a1)::constructor(), this = 0xbff3512c
A(a2)::constructor(), this = 0xbff35130
A(a1)::copy-constructor(), this = 0xbff350e8
A(a1)::copy-constructor(), this = 0xbff35138
C(c1)::constructor()
A(a1)::destructor(), this = 0xbff350e8
A(a3)::constructor(), this = 0xbff3513c
B(b1)::constructor()
B(b1)::destructor()
A(a3)::destructor(), this = 0xbff3513c
C(c1)::destructor()
A(a1)::destructor(), this = 0xbff35138
A(a2)::destructor(), this = 0xbff35130
A(a1)::destructor(), this = 0xbff3512c

オンラインで試す

ご覧のとおり、c1 のコンストラクターにパラメーターとして a1 を渡すと 1 つのコピー構築が発生し、このコンストラクターがそのメンバー a を初期化するときに 2 つ目のコピー構築が発生します。一時コピーは直後に破棄されますが、 c が破棄されるとメンバーが破棄されます。

編集:
ここでは、コピー コンストラクターが作成されたときの正確なルールを読むことができます。
デフォルトのコピー コンストラクターを作成しないためには、ユーザー定義のコンストラクターを提供するだけでは十分ではなく、コピー/ムーブ コンストラクターである必要があります。

編集2:

C++14 標準 (12.8 クラス オブジェクトのコピーと移動) から取得:

7 クラス定義でコピー コンストラクターが明示的に宣言されていない場合は、暗黙的に宣言されます。クラス定義でムーブ コンストラクターまたはムーブ代入演算子が宣言されている場合、暗黙的に宣言されたコピー コンストラクターは削除済みとして定義されます。それ以外の場合は、デフォルト (8.4) として定義されます。クラスにユーザー宣言のコピー代入演算子またはユーザー宣言のデストラクタがある場合、後者のケースは推奨されません。

于 2016-02-20T13:18:14.450 に答える