2016-08-17 16 views
3

私はstd::vectorのカスタムクラスを持っています(簡略化のため、サンプルではintを使用しています)。私はベクトルのメンバに参照/ポインタ/リンク/その他を保持したいと思います。ただし、ベクターには要素が削除されて追加されることがよくあります。ベクターへの変更後、ベクトル要素への参照/ポインタ/リンクを維持する方法は?

私の要点を説明するために、以下のサンプルでは、​​参照またはベクトルの2番目の要素へのポインタのいずれかを取ります。私は参照/ポインタを使用して、選択した要素の値を増やします。最初の要素を消去し、ref /ポインタを使って再度インクリメントします。

参考例:

std::vector<int> intVect = {1,1,1}; 
int& refI = intVect.at(1); 
refI++; 
intVect.erase(intVect.begin()); 
refI++; 

スマートポインタの例:私は起こるしたい何

std::vector<int> intVect2 = {1,1,1}; 
std::shared_ptr<int> ptrI = std::make_shared<int>(intVect2.at(1)) ; 
*ptrI = *ptrI +1; 
intVect2.erase(intVect2.begin()); 
*ptrI = *ptrI +1; 

は3の値を持つように参照される要素で終わるし、最終的なベクター{3,1}で構成されています。しかし、参考例では、最終ベクトルは{2,2}であり、ポインタ例では最終ベクトルは{1,1}である。

ポインタが本質的にメモリアドレスであることを理解すると、なぜこの方法が不可能なのか理解できますが、どういうわけか、私に教えてください。

さらに重要な質問は、実行可能な要素(値またはオブジェクト)にref/pointer/link/otherの何らかの形式を許可する代替アプローチまたは構造を使用できますそれを含むベクトル(または他の構造)にメンバーを追加したり、メンバーを削除したりした後余分な信用のため

:私は実際に働いている

オブジェクトがposition性質を持っています。どのオブジェクトがどの位置にあるのかをすばやく調べるためにオブジェクトを追跡する必要がある2番目の構造があります。私は現在、可能な位置を表すためにグリッド(ベクトルのベクトル)を使用しています。それぞれの位置は、現在その位置にあるオブジェクトのオブジェクトのベクトルにインデックスを保持しています。しかし、オブジェクトがベクターから削除されると(非常に頻繁に起こり、反復ごとに数百回まで)、私の現在の措置は、すべてのグリッド位置をループし、削除されたインデックスよりも大きなインデックスを減らすことです。これは遅くて不器用です。文脈におけるこの問題に関する追加の考えは高く評価されるが、私の重要な質問は上記の例に関係する。

+0

'消去(開始)は'すべてのイテレータ、ポインタ、および参照を無効にするのであなたの二つの例がUBです。イテレータではなく、_index_を保存して、必要に応じて調整することはできますか?私は最近、私が使用していたイテレータを無効にする危険性があることを知ったところで同様の問題を抱えていました - そして、再割り当てなどを心配する年齢を過ごした後で、必要な要素がどの要素がインデックスであるかあなたのケースは、後でそれに戻るために)でした。だから、ベクトルが再割り当てされたかどうかは今や問題ではなく、私のコードはうまく動作します。 –

+0

@underscore_dまた、 'erase'の後にインデックスが無効になることがある(または有効であるが、別の要素にインデックスする) – mvidelgauz

+0

@mvidelgauzはい、私はOPがインデックスの調整方法を知っていると言って編集しました。これは、バッファ全体が操作中に移動された可能性があるため、イテレータまたはポインタを後で調整できると仮定するのではなく、定義された動作です。 –

答えて

1

可能な選択肢の1つは、ベクトルストアstd::shared_ptrオブジェクトを持ち、問題のオブジェクトを参照するのにstd::weak_ptrまたはstd::shared_ptrオブジェクトを発行することです。

プリントアウトしなければならない
std::vector<std::shared_ptr<int>> ints; 
for(size_t i = 0; i < 10000; i++) { 
    ints.emplace_back(std::make_shared<int>(int(i))); 
} 
std::weak_ptr<int> my_important_int = ints[6000]; 
{ 
    auto lock = my_important_int.lock(); 
    if(lock) std::cout << *lock << std::endl; 
    else std::cout << "index 6000 expired." << std::endl; 
} 

auto erase_it = std:remove_if(ints.begin(), ints.end(), [](auto & i) {return (*i) > 5000 && ((*i) % 4) != 0;}); 
ints.erase(erase_it, ints.end()); 

{ 
    auto lock = my_important_int.lock(); 
    if(lock) std::cout << *lock << std::endl; 
    else std::cout << "index 6000 expired." << std::endl; 
} 

ints.erase(ints.begin(), ints.end()); 

{ 
    auto lock = my_important_int.lock(); 
    if(lock) std::cout << *lock << std::endl; 
    else std::cout << "index 6000 expired." << std::endl; 
} 

6000 
6000 
index 6000 expired. 
2

キーと値のペアを格納するコンテナが役立ちます。たとえば、std::mapまたはstd::unordered_mapです。

これらのコンテナを使用する場合は、キーを保存して目的のオブジェクトへの参照を保持します。上記のオブジェクトを修正したい場合は、キーを使用してコンテナ内を参照してください。これで、問題のオブジェクトに影響を与えずに、他のオブジェクトを追加/削除することができます(追加/削除されたオブジェクトには固有のキーがあるものとします)。

+1

'std :: list'はもっと良いかもしれません。' map'を使うとキーに対する要件が追加されるということです。OPの場合は意味がないので、人工的/恣意的にプログラムのデザインに移植する必要があります。対照的に、 'list'では、要素に対する安全なイテレータ/ポインタ/参照を保持することができ、その特定の要素を' erase() 'するだけで無効になります。Btw、彼らは 'map'でも同じ保証を得ますが、何も使われない可能性のあるキーが追加されました。 –

2

ベクターを使い続け、オブジェクトの管理方法を変更する方法がある場合、現在のものよりもはるかにパフォーマンスが向上します。

安定したベクター(here's the boost version)を使用することができます。基本的にポインタのベクトルであり、イテレータと参照の安定性を与えます。つまり、イテレータ(ポインタ)と要素への参照は、要素自体を削除する以外の操作では無効化されません。

もちろん、主にパフォーマンスに大きな欠点があります。 2つの主要なパフォーマンスの問題は、要素にアクセスするたびにポインタを渡すという事実と、要素が連続して格納されていないという事実です(もちろん反復速度に影響します)。

しかし、他のポインタの重いデータ型(リスト、セット、マップ)にも利点があります。主に、法線ベクトルよりも遅いものの、一定時間内にルックアップとプッシュバックを実行します。

また、パフォーマンスが本当に必要な場合は、ベクターを保存してデザインを再考することもできます。

関連する問題