设计模式,用于创建对向量中元素的智能引用
Design pattern to create smart references to elements in a vector
由于vector中的引用指向内存的位置而不是抽象元素,因此在更改vector的内存时可能会导致一些问题。
-
如果引用指向vector中的一个元素,然后将该元素洗牌到vector中的另一个位置,则引用不跟踪该元素,并且在洗牌后将指向错误的数据。
-
如果一个元素是无效的,你仍然可以访问该元素的内容,而不需要任何安全检查,如果你在元素无效之前声明了一个引用。
-
如果矢量大小调整,所有当前引用可能无效
我写了一个示例程序来演示这三个问题。
#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;
}
是否存在一种设计模式来创建智能引用或代理类型来解决这些问题?一个对象,将跟踪其元素在向量中的位置,特定的行为,如果元素是活的或死的,并在调整大小后保持有效?我很好智能/代理引用的实现不是一个实际的引用,可以是一个指针,整数索引,或任何东西。但这是专门针对vector中的元素,而不是链表、map等。
使用std::vector<std::shared_ptr<entity>>
,您可能拥有您想要的安全性:
class manager {
std::vector<std::shared_ptr<entity>> vec;
public:
//Reserves initial_amount of entities
explicit manager(size_t initial_amount) { vec.reserve(initial_amount); }
std::weak_ptr<entity> create(float f) {
vec.push_back(std::make_unique<entity>(entity{true, f}));
return vec.back();
}
void refresh() {
vec.erase(std::remove_if(vec.begin(), vec.end(),
[](const auto& ent) {return !ent->alive;}),
vec.end());
}
void grow(size_t n) { vec.reserve(n); }
void print() { //Prints all alive entities.
for (const auto& ent : vec)
std::cout << ent->data << " ";
std::cout << std::endl;
}
};
然后测试:
int main() {
manager c(10);
auto d1 = c.create(5.5);
auto d2 = c.create(10.5);
auto d3 = c.create(7.5);
// Correct behavior
if (auto e = d1.lock()) std::cout << e->data << std::endl; else std::cout << "Dien"; // 5.5
if (auto e = d2.lock()) std::cout << e->data << std::endl; else std::cout << "Dien"; // 10.5
if (auto e = d3.lock()) std::cout << e->data << std::endl; else std::cout << "Dien"; // 7.5
std::cout << std::endl;
if (auto e = d2.lock()) e->alive = false; // "Kill" the entity
c.refresh(); // removes all dead entities.
if (auto e = d1.lock()) std::cout << e->data << std::endl; else std::cout << "Dien"; // 5.5
if (auto e = d2.lock()) std::cout << e->data << std::endl; else std::cout << "Dien"; // Die
if (auto e = d3.lock()) std::cout << e->data << std::endl; else std::cout << "Dien"; // 10.5
std::cout << std::endl;
c.print(); // Correct behavior, prints only alive entities.
std::cout << std::endl;
if (auto e = d3.lock()) e->data = 6.5; // Trying to change the value of d3,
// which should still be alive.
c.print();
std::cout << std::endl;
c.grow(10000);
if (auto e = d1.lock()) std::cout << e->data << std::endl; else std::cout << "Dien"; // 5.5
if (auto e = d2.lock()) std::cout << e->data << std::endl; else std::cout << "Dien"; // Die
if (auto e = d3.lock()) std::cout << e->data << std::endl; else std::cout << "Dien"; // 6.5
}
演示相关文章:
- Mongodb c++驱动程序:如何查询元素的数组
- 1d 智能指针不适用于语法 (*)++
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 使用strcpy将char数组的元素复制到另一个数组
- 使用不带参数的函数访问结构元素
- 给定n个元素的m个集合.在C++中找到出现在最大集合数中的元素
- C++如何通过用户输入删除列表元素
- lower_bound()返回最后一个元素
- 基于多个条件处理地图中的所有元素
- 调整大小后指向元素值的指针unordered_map有效?
- 使用std::transform将一个范围的元素添加到另一个范围中
- 使用函数"remove"删除重复元素
- 具有最大子序列大小的序列,每个元素都相同
- 如何将两个不同矢量的同一位置的两个元素组合在一起
- std::array :通过智能指针访问元素的成员
- 如何将元素添加到由智能指针控制的数组中
- 智能感知:数组可能没有这种类型的元素
- 智能指针std::对的元素
- 自定义UI元素使用的智能指针选择
- 设计模式,用于创建对向量中元素的智能引用