13

C++がベクター内のオブジェクトを管理する方法について混乱しています。私が次のことをしたとしましょう:

vector<MyClass> myVector;
myVector.push_back(a);
myVector.push_back(b);
MyClass & c = myVector[1];
myVector.erase(myVector.begin());

参照cはまだ有効ですか(またはさらに良いことに、有効であることが保証されています)?そうでない場合は、安全を確保するために、常に参照からコピーを作成する必要がありますか?

4

4 に答える 4

12

JavaまたはC#参照(C++参照よりもC++ポインターに似ています)とは異なり、C ++の参照はポインターと同じくらい「ダム」です。つまり、オブジェクトの参照を取得し、そのオブジェクトをメモリ内で移動すると、その参照ははもう有効ではありません。

参照cはまだ有効ですか(またはさらに良いことに、有効であることが保証されています)?

あなたが説明している場合、標準ベクトルは、ベクトルの内容が変更されたとき(アイテムの削除、ベクトルのサイズ変更など)に、含まれているオブジェクトをメモリ内の同じ場所に保持することを保証されません。

これにより、イテレータと、含まれているオブジェクトへのポインタ/参照の両方が無効になります。

そうでない場合は、安全を確保するために、常に参照からコピーを作成する必要がありますか?

適切なオブジェクトを「ポイント」し続けるには複数の方法があり、それらはすべて、あるレベルの間接参照を意味します。

フル/バリューコピー

最も簡単なのは、MyClassの完全なコピーを作成することです。

vector<MyClass> x ;
x.push_back(a) ;
x.push_back(b) ;
MyClass c = x[1] ;    // c is a full copy of b, not a reference to b
x.erase(x.begin()) ;

適切なコンテナを使用する

2番目に簡単な方法はstd::list、要素の挿入と削除用に特別に設計されたaを使用することです。これは、含まれているオブジェクトを変更したり、それらへのポインター、参照、またはイテレーターを無効にしたりしません。

list<MyClass> x ;
x.push_back(a) ;
x.push_back(b) ;
list<MyClass> it = x.begin() ;
++it ;
MyClass & c = *it ;
x.erase(x.begin()) ;

ポインタの使用(安全ではない)

もう1つは、オブジェクトの代わりにへのstd::vector<MyClass *>ポインタを含むを作成することです。これにより、(余分な間接参照のために)わずかに異なる表記法で、ポイントされたオブジェクトへのポインターまたは参照を保持できるようになります。MyClassMyClass

vector<MyClass *> x;
x.push_back(a);     // a being a MyClass *
x.push_back(b);     // b being a MyClass *
MyClass * c = x[1]; // c points to the same object as b
x.erase(x.begin()); // note that a will still need separate deallocation

オブジェクトaとbの明確な(コンパイラに関する限り)所有者がいないため、これは安全ではありません。つまり、オブジェクトが不要になったときにオブジェクトの割り当てを解除する明確なコードがないことを意味します(これがメモリリークの発生方法です)。 CおよびC++で)

したがって、この方法を使用する場合は、コードが適切にカプセル化されていることを確認し、メンテナンスの予期せぬ事態を避けるために可能な限り小さくしてください。

スマートポインタの使用(より安全)

より良いものは、スマートポインタを使用することです。たとえば、C ++ 11(またはブースト)を使用しshared_ptrます。

vector< shared_ptr<MyClass> > x;
x.push_back(a);               // a being a shared_ptr<MyClass>
x.push_back(b);               // b being a shared_ptr<MyClass>
shared_ptr<MyClass> c = x[1]; // c points to the same object as b
x.erase(x.begin());           // No deallocation problem

さて、を使用していて、shared_ptrについて何も知らないweak_ptr場合は、問題があるので、そのギャップを埋める必要があります。

スマートポインタの使用2(より安全)

unique_ptr別の解決策は、ポイントされたオブジェクトの排他的な所有者であるC++11を使用することです。したがって、ポインタまたはポインタオブジェクトへの参照が必要な場合は、生のポインタを使用する必要があります。

vector< unique_ptr<MyClass> > x;
x.push_back(a);               // a being a unique_ptr<MyClass>
x.push_back(b);               // b being a unique_ptr<MyClass>
MyClass * c = x[1].get();     // c points to the same object as b
x.erase(x.begin());           // No deallocation problem

ここで、上記の。の場合とは異なり、ベクトルはオブジェクトの一意の所有者であることに注意してくださいsmart_ptr

結論

C ++でコーディングしているため、問題に適した方法を選択する必要があります。

ただし、最初に、ポインターによって追加される間接参照のレベル、ポインターの機能、およびC ++参照の機能(およびC#/ Java参照ではない理由)を確実に理解する必要があります。

于 2012-10-28T19:58:49.103 に答える
10

のリファレンスvectorから:

[5]ベクトルのイテレータは、そのメモリが再割り当てされると無効になります。さらに、ベクトルの途中で要素を挿入または削除すると、挿入または削除ポイントに続く要素を指すすべてのイテレータが無効になります。したがって、reserve()を使用して、ベクターが使用するメモリを事前に割り当て、すべての挿入と削除がベクターの最後にある場合は、ベクターのイテレーターが無効になるのを防ぐことができます。

したがって、イテレータは無効になり、以降はerase使用できません。

于 2012-10-28T19:36:48.057 に答える
3

一般にベクトルの位置またはサイズを変更すると、ベクトルの内容への参照は無効になります。いくつかの例外があります。データを使用する前に、ベクター内のデータの位置、またはベクター内の要素のサイズまたは数を変更する場合にのみ、コピーを作成する必要があります。

于 2012-10-28T19:35:02.310 に答える
3

参照cはまだ有効ですか(またはさらに良いことに、有効であることが保証されています)?

いいえ、現在は未定義の状態です。

そうでない場合は、安全を確保するために、常に参照からコピーを作成する必要がありますか?

取得してから使用するまでの間に削除または移動(など)される可能性がある場合は、コピーを作成する必要があります。

于 2012-10-28T19:35:49.740 に答える