9
#include <iostream>
#include <utility>
#include <vector>

int i = 0;
struct A
{
    A() : j( ++i )
    {
        std::cout<<"constructor "<<j<<std::endl;
    }
    A( const A & c) : j(c.j)
    {
        std::cout<<"copy "<<j<<std::endl;
    }
    A( const A && c) : j(c.j)
    {
        std::cout<<"move "<<j<<std::endl;
    }
    ~A()
    {
        std::cout<<"destructor "<<j<<std::endl;
    }

    int j;
};

typedef std::vector< A > vec;

void foo( vec & v )
{
    v.push_back( std::move( A() ) );
}

int main()
{
    vec v;

    foo( v );
    foo( v );
}

上記の例では、次の出力が生成されます。

constructor 1
move 1
destructor 1
constructor 2
move 2
move 1
destructor 1
destructor 2
destructor 1
destructor 2

質問:

  1. 最初のデストラクタが実行されるのはなぜですか (ただし、2 番目のオブジェクトに対しては実行されません)。
  2. 1 番目のオブジェクトの移動の前に 2 番目のオブジェクトの移動が実行されるのはなぜですか?
  3. 最後に各オブジェクトに対して 2 つのデストラクタが実行されるのはなぜですか?

PS確認したところ、オブジェクトは実際に期待どおりに配置されています(1番目はベクトルの位置0に移動し、2番目はベクトルの位置1に移動します)

PPS問題があれば、私はgcc 4.3を使用しており、次のようにプログラムをコンパイルします:

g++ n1.cpp -Wall -Wextra -pedantic -ansi -std=c++0x -O3
4

4 に答える 4

10

私はあなたの例を少し書き直しました:

#include <iostream>
#include <utility>
#include <vector>

int i = 0;
struct A
{
    A() : j( ++i )
    {
        std::cout<<"constructor "<<j<<std::endl;
    }
    A( const A & c) : j(c.j)
    {
        std::cout<<"copy "<<j<<std::endl;
    }
    A( A && c) : j(c.j)
    {
        std::cout<<"move "<<j<<std::endl;
    }
    ~A()
    {
        std::cout<<"destructor "<<j<<std::endl;
    }

    int j;
};

typedef std::vector< A > vec;

void foo( vec & v )
{
    v.push_back( A() );
}

int main()
{
    vec v;
    std::cout << "A\n";
    foo( v );
    std::cout << "B\n";
    foo( v );
    std::cout << "C\n";
}
  1. const移動コンストラクターからを削除しました。
  2. を削除しstd::moveましたpush_back(不要です)。
  3. の呼び出しの間にマーカーを挿入しましたfoo

私にとって、これはあなたのコードと同じように出力されます:

A
constructor 1
move 1
destructor 1
B
constructor 2
move 2
copy 1
destructor 1
destructor 2   // 1
C
destructor 2
destructor 1
  1. 1番目のデストラクタが実行されるのはなぜですか(ただし、2番目のオブジェクトに対しては実行されません)。

2番目のデストラクタは、でマークされた行の2番目のオブジェクトに対して実行され// 1ます。A()これは、への2回目の呼び出しの終了時の一時的な破棄ですpush_back

  1. 1番目のオブジェクトの移動の前に2番目のオブジェクトの移動が実行されるのはなぜですか?

注:私にとって、最初のオブジェクトは移動ではなくコピーされます。これについては以下で詳しく説明します。

回答:例外安全性。

説明:この間push_backに、ベクターは(1つの)バッファーがいっぱいであることを検出し、2つのスペースを持つ新しいバッファーを作成する必要があります。新しいバッファを作成します。次に、2番目のオブジェクトをそのバッファー(その最後)に移動します。その構造が例外をスローした場合、元のバッファーはそのままvector残り、変更されません。それ以外の場合、要素は最初のバッファーから2番目のバッファーに移動またはコピーされます(したがって、最初の要素を2番目に移動/コピーします)。

移動コンストラクターがある場合Aは、 aを使用して古いバッファーから新しいバッファーに移動します。ただし、moveコンストラクターがそうでない場合は、aが使用されます。これも例外安全のためです。古いバッファから新しいバッファへの移動が失敗する可能性がある場合は、を元の状態に復元できるように、古いバッファをそのままにしておく必要があります。noexceptmovenoexceptcopyvector

noexcept移動コンストラクターに追加する場合:

A( A && c) noexcept : j(c.j)
{
    std::cout<<"move "<<j<<std::endl;
}

次に、私の出力は次のとおりです。

A
constructor 1
move 1
destructor 1
B
constructor 2
move 2
move 1
destructor 1  // 2
destructor 2
C
destructor 2
destructor 1

マークされた行// 2は、新しいバッファに移動して構築された後の、古いバッファからの最初の要素の破棄であることに注意してください。

  1. 最後に、オブジェクトごとに2つのデストラクタが実行されるのはなぜですか?

これは、の各要素のvector破壊、したがっての破壊を示しています。vector

于 2011-10-06T13:08:46.457 に答える
5

の賢明な使用はreserve、問題の半分を解決します: http://ideone.com/5Lya6 (明示的に要求しない) 予期しない移動の数を減らすことによって

また、temp のデストラクタは、ベクトルに移動したも起動することを忘れないでください。これが、移動の割り当て/構築の後でも、temp が正常で破壊可能な状態ままであることを確認する必要がある理由です。

#include <iostream>
#include <utility>
#include <vector>

int i = 0;
struct A
{
    A() : j( ++i )
    {
        std::cout<<"constructor "<<j<<std::endl;
    }
    A( const A & c) : j(c.j)
    {
        std::cout<<"copy "<<j<<std::endl;
    }
    A( const A && c) : j(c.j)
    {
        std::cout<<"move "<<j<<std::endl;
    }
    ~A()
    {
        std::cout<<"destructor "<<j<<std::endl;
    }

    int j;
};

typedef std::vector< A > vec;

void foo( vec & v )
{
    v.push_back( std::move( A() ) );
}

int main()
{
    vec v;
    v.reserve(2);

    foo( v );
    foo( v );
}
于 2011-10-06T08:23:45.750 に答える
4

ベクトルは、 の呼び出し中にその容量を増やし、内部要素を移動していますpush_back

于 2011-10-06T08:18:46.043 に答える
2

移動コンストラクターは、移動されたオブジェクトを「破棄」しません。

#include <iostream>

struct Foo { 
  int i;
  bool active;

  Foo(int i): i(i), active(true) {}
  Foo(Foo&& rhs): i(rhs.i), active(rhs.active) { rhs.active = false; }
  Foo(Foo const& rhs): i(rhs.i), active(rhs.active) {}
  ~Foo() { std::cout << i << (active ? " active": " inactive") << "\n"; }
};


int main() {
  Foo foo;
  Bar bar(std::move(foo));
}

出力は次のとおりです。

1 active
1 inactive
于 2011-10-06T08:24:59.383 に答える