4
#include <iostream>
#include <vector>
#include <cstdlib>
#include <cassert>

struct s_A {
    bool bin;
    s_A(): bin(0) {}
};

class c_A {
public:
    s_A * p_struct;

    c_A(): p_struct(NULL) {p_struct = new s_A [16];}

    void Reset()
    {
        delete [] p_struct;
        p_struct = new s_A [16];
    }
};

int main () 
{   
    srand(1);
    int x = 30;
    std::vector <c_A> objects;
    objects.assign(x, c_A());
    std::vector <c_A> objects_copy;

    for(int q=0; q < x; q++)
    {
        objects_copy.push_back(objects[ rand() % x ]);
        objects_copy[q].Reset();
    }

    for(int q=0; q < 16; q++)
        for(int w=0; w < x; w++)
        {
            // Assertion should not fail, but it does
            assert(!objects_copy[w].p_struct[q].bin);
            objects_copy[w].p_struct[q].bin = true;
        }
}

どういうわけか、コピーされたさまざまなオブジェクトのポインターが同じメモリを指すことになり、アサーションは最終的に失敗します。コピーされていないベクトルで実行した場合、この動作は発生しません。c_A.Reset() は新しい配列を指すように (delete[] を介して) ポインターを解放する必要があると考えましたが、明らかに何かが欠けています。

4

1 に答える 1

5

問題の特定の原因は、次の行です。

objects_copy.push_back(objects[ rand() % x ]);
objects_copy[q].Reset();

問題は、オブジェクトのコピーを にプッシュしようとすると、 にオブジェクトobjects_copyの浅いコピーが作成されることobjects vectorです。これは、2 つのベクトル内のオブジェクトが相互のコピーであるポインターを持つことになることを意味します。Resetしたがって、の要素で呼び出すと、配列objects_copy vectorの要素によってまだ指されているメモリの割り当てが解除されます。objects

問題は、クラスが3 の規則にc_A違反していることです。クラスはリソースをカプセル化するため、デストラクタ、コピー コンストラクタ、およびコピー代入演算子が必要です。これら 3 つの関数を定義すると、オブジェクトを にコピーしようとすると、おそらく複製または参照カウントによって、基になるリソースを管理できるようになります。これらの関数の書き方の詳細については、これらの関数の書き方の説明を参照してくださいobjects_copy vector

編集:何が起こっているのかについてのより詳細な説明は次のとおりです。

問題は、オブジェクトを に追加するときに、vector実際にはそのオブジェクトを に格納していないことですvector。むしろ、そのオブジェクトのコピーを保存しています。したがって、あなたが書くときobjects_copy.push_back(objects[ rand() % x ]);、あなたは両方に同じオブジェクトを格納していませんvectors。代わりに、 からオブジェクトの 1 つのコピーを作成し、objectsそれを に保存していobjects_copyます。あなたのc_A型にはコピー関数が定義されていないため、オブジェクトの浅いコピーが作成され、ポインターのコピーが作成されます。objectsこれは、リストの元のオブジェクトと の対応するコピーについて考えると、objects_copyそれぞれに同じp_structポインタのコピーがあることを意味します。Resetでオブジェクトを呼び出すと、objects_copy vector、そのポインタが指すメモリを解放します。ただし、 に格納されている元のオブジェクトのポインターを更新していないobjectsため、そのポインターはガベージ メモリを参照しています。そのポインターを使用しようとすると、未定義の動作が発生するため、クラッシュします。

コピー機能を追加すると、コピーの作成方法を制御できるようになり、これが修正されます。コピー関数を定義して、コピーc_Aが元のオブジェクトが指すオブジェクトの新しいコピーを指すようにする場合、各オブジェクトには独自の個別のポインターがあるため、この問題は発生しません。または、参照カウントを使用する場合、他のオブジェクトがそれを指していることがわかっている場合は、リソースを削除しないことで問題を回避できます。

お役に立てれば!

于 2011-06-01T22:45:02.357 に答える