4

Visual Studio 2010C++コンパイラで奇妙な動作に遭遇しました。次のコードはコンパイルされますが、実行後に「デバッグアサーションに失敗しました」というメッセージが表示されます。

"_BLOCK_TYPE_IS_VALID(pHead-> nBlockUse)"

GCCでコンパイルしてスムーズに実行します。それは私のせいですか?

#include <iostream>
#include <vector>


using namespace std;

typedef unsigned int uint;


class Foo {
    vector<int*> coll;
public:

    void add(int* item) {
       coll.push_back(item);
    }

    ~Foo() {
        for (uint i = 0; i < coll.size(); ++i) {
            delete coll[i];
            coll[i] = NULL;
        }
    }
};

int main()
{
   Foo foo;
   foo.add(new int(4));
   Foo bar = foo;

   return 0;
}
4

4 に答える 4

7

コピーコンストラクターとコピー代入演算子を実装していません(3つのルールを参照)。これにより、ベクター内のポインターのコピーが浅くなり、二重削除とアサーションが発生します。編集:二重削除は未定義の動作であるため、VSとgccの両方がここで正しいので、好きなことを行うことができます。

通常、重要な動作をするデストラクタを実装する場合は、コピーの構築とコピーの割り当てを作成または無効にする必要もあります。

しかし、あなたの場合、本当にポインタでアイテムを保存する必要がありますか?そうでない場合は、値で保存するだけで問題が解決します。それ以外の場合、ポインタが必要な場合はshared_ptr、生のポインタの代わりに(コンパイラまたはブーストから)使用して、独自のデストラクタ/コピーメソッドを作成する必要をなくします。

編集:インターフェースに関する追加の注意:渡されたポインターの所有権を譲渡するこのようなインターフェースは、クラスを使用する人々に混乱を引き起こす可能性があります。誰かがヒープに割り当てられていないintのアドレスを渡した場合でも、デストラクタは失敗します。可能であれば値で受け入れるかnewadd関数内で独自の呼び出しを行う渡されたアイテムのクローンを作成することをお勧めします。

于 2011-05-06T15:54:27.577 に答える
3

行が原因で、アイテムを2回削除しています

Foo bar = foo;

データを割り当ててコピーするのではなく、itempointerを複製するデフォルトのコピーコンストラクターを呼び出します。

于 2011-05-06T15:54:26.267 に答える
2

問題は、barfooメンバーの両方のベクトル要素が同じであるということです。スコープ外にfooなると、デストラクタが呼び出され、ポインタの割り当てが解除され、barベクトル要素がぶら下がったままになります。barデストラクタは、ぶら下がったままになっているベクトル要素の割り当てを解除しようとし、実行時エラーを引き起こします。コピーコンストラクターを作成する必要があります。

Foo bar = foo; // Invokes default copy constructor.

編集1:このスレッドを見て、三つのルールについて知ってください

于 2011-05-06T15:54:44.143 に答える
0

ここでのより簡単な解決策はint*、そもそも使用しないことです。

#include <iostream>
#include <vector>


using namespace std;

typedef unsigned int uint;


class Foo {
    vector<int> coll; // remove *
public:

    void add(int item) { // remove *
       coll.push_back(item);
    }

    // remove ~Foo
};

int main()
{
   Foo foo;
   foo.add(4); // remove `new` call
   Foo bar = foo;

   return 0;
}

一般的に、を避けるようにしてくださいnew

できない場合は、スマートマネージャー(などstd::unique_ptr)を使用してメモリのクリーンアップを処理してください。

いずれにせよ、delete手動で呼び出している場合は、間違っています注:呼び出さずdeleteにメモリリークを発生させるのも間違っています

于 2011-05-06T16:34:58.557 に答える