2

このコードを書いて、C++ でデストラクタの動作を調べました

#include <vector>
#include <iostream>

using namespace std;


class WrongDestructor
{
private:
    int number;
public:
    WrongDestructor(int number_) :
    number(number_)
    {}

    ~WrongDestructor() {

    cout<<"Destructor of " <<number<<endl;

//  throw int();
    }
};
int main(int argc, char *argv[])
{
    std::vector<WrongDestructor> wrongs;

    for(int i = 0; i < 10; ++i) {
    wrongs.push_back(WrongDestructor(i));
    }

    return 0;
}

私が興味深いと思ったのは、私のプログラムの出力です:

Destructor of 0
Destructor of 0
Destructor of 1
Destructor of 0
Destructor of 1
Destructor of 2
Destructor of 3
Destructor of 0
Destructor of 1
Destructor of 2
Destructor of 3
Destructor of 4
Destructor of 5
Destructor of 6
Destructor of 7
Destructor of 0
Destructor of 1
Destructor of 2
Destructor of 3
Destructor of 4
Destructor of 5
Destructor of 6
Destructor of 7
Destructor of 8
Destructor of 9
Destructor of 0
Destructor of 1
Destructor of 2
Destructor of 3
Destructor of 4
Destructor of 5
Destructor of 6
Destructor of 7
Destructor of 8
Destructor of 9

思ったよりもはるかに多くのオブジェクトが作成されているということです。コレクションには明らかに 10 があり、for ループでコレクションを埋めるときに次の 10 が一時オブジェクトとして作成されると予想していました。しかし、それらの数は多く、他のものよりも頻繁に作成されるものもあります。

4

4 に答える 4

4

要素を保持するためにより大きなメモリブロックを割り当てる必要がある場合vector、新しい要素は新しいより大きなメモリブロックに移動構築されます。タイプは移動コンストラクターまたはコピーコンストラクターを定義しないため、代わりにコンパイラーが提供するデフォルトのコピーコンストラクターを取得します。デフォルトのコピーコンストラクターは、クラス内のすべてのメンバーの単純なメンバーごとのコピーを実行します。

さらに、を使用して要素自体を挿入するには、要素をpush_backに移動またはコピーする必要がありvectorます。その結果、コンパイラがこれを最適化しないと仮定すると、そこにもコピーが作成されます。(を使用すると、これらのコピーを回避できることに注意してくださいemplace_back。)

その結果、インスタンスの複数のコピーがコンテナに早期に挿入されます。たとえば、1内部のより大きなメモリバッファにコピーされるvectorと、古い、より小さなバッファで破棄されるためです。

コピーおよび/または移動コンストラクターを定義することで、この動作をより明確に確認できます。

#include <vector>
#include <iostream>

using namespace std;


class WrongDestructor
{
private:
    int number;
public:
    WrongDestructor(int number_) :
    number(number_)
    {}

    // Copy constructor
    WrongDestructor(WrongDestructor const& copied)
        : number(copied.number)
    {
        cout << "Copied " << this->number << endl;
    }

    ~WrongDestructor() {

    cout<<"Destructor of " <<number<<endl;

//  throw int();
    }
};
int main(int argc, char *argv[])
{
    std::vector<WrongDestructor> wrongs;

    for(int i = 0; i < 10; ++i) {
    wrongs.push_back(WrongDestructor(i));
    }

    return 0;
}

このプログラムは次の出力を提供します:http://ideone.com/S5Zf41

于 2012-12-09T08:31:02.690 に答える
3

ベクトルのサイズは事前に決定されていません。オブジェクトをベクターにプッシュバックするとき、新しいエントリに対応するためにベクター自体を再割り当てする必要があります。これは、オブジェクトをメモリバッファから新しいバッファにコピーすることを意味します。エルゴ、これを行うと、より多くのコピーが表示されます。

于 2012-12-09T08:30:42.200 に答える
1

std::vector::reserveベクトルに十分なスペースを割り当てるために使用できます。それ以外の場合、ベクトルは、大量のコピーを生成する着信要素の連続スペースを再割り当てする必要があります。

std::vector<WrongDestructor> wrongs;
wrongs.reserve(10);
于 2012-12-09T08:32:50.507 に答える
1

はい、ベクトルに 10 個のオブジェクトを追加したように見えますが、実際にはさらに 10 個のオブジェクトが作成されているため、最後にさらに 10 個のオブジェクトが破棄されます。

ベクトルのコンテンツがますます増加すると、ベクトルはより多くのオブジェクトの容量を増加させることを知っておく必要があります。このプロセス中に何が起こったのでしょうか?

ベクトルに新しいオブジェクト用のスペースがない場合、より多くのオブジェクト用に大きなメモリ ブロックが作成され、そのオブジェクトがコピーされます。次に、(push_back によって) 新しいオブジェクトを追加すると、オブジェクトが破棄され、元のメモリが解放されます。

したがって、作成されたオブジェクトをさらにコピーすると、元のオブジェクトを破棄すると、さらにデストラクタが呼び出されます。

最善の方法は、copy-constructor を提供し、その中に何かを printf することです。また、ctorに何かを出力すると、プロセス全体が表示されます。

さらに知っておく必要があるのは、ベクターの容量と予備です。

于 2012-12-09T08:42:22.747 に答える