ベクトル内の参照は抽象要素ではなくメモリの位置を指すため、ベクトルのメモリを変更するときに問題が発生することがあります。ベクトル内の要素へのスマートな参照を作成するデザインパターン
ベクトルの要素を基準点であれば、その後、その要素は、ベクトル内の別の場所にシャッフルされ、参照が要素を追跡していない、とシャッフルした後、間違ったデータを指すようになります。
要素が無効化されている場合、要素を無効化する前に参照を宣言した場合、その要素の内容に安全性チェックなしでアクセスできます。
ベクトルのサイズを変更すると、すべての現在の参照が無効になることがあります。
私は3つの問題すべてを示すサンプルプログラムを作成しました。
#include <iostream>
#include <vector>
struct entity { //Simple struct of data.
bool alive;
float data;
};
class manager {
std::vector<entity> vec;
size_t count; // Amount of currently alive entities
public:
//Reserves initial_amount of entities, all set to dead, count set to 0.
manager(size_t initial_amount) : vec(initial_amount, { false, 0.0f }), count(0) {}
entity& create(float f) {
vec[count] = {true, f};
return vec[count++];
}
void refresh() { //Two iterators, one starts at the front of the vector, the other at
size_t front = 0; //count. The front iterator searches for dead entities and swaps them
size_t back = count; //with alive entities from the back iterator. For each swap we decrement
//count by 1, with the final result being all alive entities are between
while(true) { //0 and count.
for(; true; ++front) {
if (front > back) return;
if (!vec[front].alive) break;
}
for(; true; --back) {
if (vec[back].alive) break;
if (back <= front) return;
}
std::swap(vec[back], vec[front]);
--count;
++front;
--back;
}
}
void grow(size_t n) {
vec.resize(n);
}
void print() { //Prints all alive entities.
for (size_t index = 0; index < count; index++)
std::cout << vec[index].data << " ";
std::cout << std::endl;
}
};
int main() {
using namespace std;
manager c(10);
entity& d1 = c.create(5.5);
entity& d2 = c.create(10.5);
entity& d3 = c.create(7.5);
// Correct behavior
cout << d1.data << endl; // 5.5
cout << d2.data << endl; // 10.5
cout << d3.data << endl; // 7.5
cout << endl;
d2.alive = false; // "Kill" the entity
c.refresh(); // removes all dead entities. (this will swap d2's and d3's data in the vector,
// but wont change the locations they point to)
// Oh no! d2 and d3 still point to the same locations in the vector and now their data
// is incorrect after the swap, also d2 is dead maybe that should just be an error.
cout << d1.data << endl; // 5.5
cout << d2.data << endl; // 7.5
cout << d3.data << endl; // 10.5
cout << endl;
c.print(); // Correct behavior, prints only alive entities.
cout << endl;
d3.data = 6.5; // Trying to change the value of d3, which should still be alive.
c.print(); // Error, because d3 still points to the 3rd slot the intended value hasn't been changed.
cout << endl;
c.grow(10000);
cout << d1.data << endl; // After resize all these references are invalidated,
cout << d2.data << endl; // and using them is undefined behavior.
cout << d3.data << endl;
return 0;
}
これらの問題を解決するスマートリファレンスまたはプロキシタイプを作成するためのデザインパターンはありますか?要素が生きているか死んでいて、サイズ変更後に有効なままであれば、ベクトル内の要素位置を追跡するオブジェクトです。
スマート/プロキシリファレンスの実装は実際の参照ではなく、ポインタ、整数インデックスなどにすることができます。しかし、これはstd::vector<std::shared_ptr<entity>>
のベクトルではなく、リンクされたリストの要素、マップなどのために特別に
言い換えれば、nope。しかし、ポインタのベクトルを持つことができます。 –