C++ 用于创建向量中元素的智能参照的设计模式

C++ 用于创建向量中元素的智能参照的设计模式,c++,vector,C++,Vector,因为向量中的引用指向内存的位置,而不是抽象元素,所以在更改向量的内存时可能会导致一些问题 如果引用指向向量中的某个元素,然后该元素被无序移动到向量中的另一个点,则引用不会跟踪该元素,并且在无序移动后将指向不正确的数据 如果元素无效,如果在元素无效之前声明了引用,则仍然可以访问该元素的内容,而无需进行任何安全检查 如果向量调整大小,则所有当前引用都可能无效 我编写了一个示例程序来演示这三个问题 #include <iostream> #include <vector> s

因为向量中的引用指向内存的位置,而不是抽象元素,所以在更改向量的内存时可能会导致一些问题

  • 如果引用指向向量中的某个元素,然后该元素被无序移动到向量中的另一个点,则引用不会跟踪该元素,并且在无序移动后将指向不正确的数据

  • 如果元素无效,如果在元素无效之前声明了引用,则仍然可以访问该元素的内容,而无需进行任何安全检查

  • 如果向量调整大小,则所有当前引用都可能无效

  • 我编写了一个示例程序来演示这三个问题

    #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::vec;
    size\u t count;//当前活动实体的数量
    公众:
    //保留实体的初始数量,所有设置为“死”,计数设置为0。
    管理器(大小和初始金额):vec(初始金额,{false,0.0f}),count(0){}
    实体和创建(浮动f){
    vec[count]={true,f};
    返回向量[count++];
    }
    void refresh(){//两个迭代器,一个从向量的前面开始,另一个从
    size\u t front=0;//count。前端迭代器搜索死实体并交换它们
    size\u t back=count;//使用来自back迭代器的活动实体。对于每个交换,我们递减
    //按1计数,最终结果是所有活动实体介于
    while(true){//0并计数。
    for(;true;++front){
    如果(前>后)返回;
    如果(!vec[front].alive)中断;
    }
    for(;true;--back){
    如果(vec[back].alive)中断;
    如果(返回使用
    std::vector
    ,您可以获得所需的安全性:

    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;
        }
    };
    
    类管理器{
    std::vec;
    公众:
    //储备实体的初始金额
    显式管理器(大小初始金额){vec.reserve(初始金额);}
    标准::弱ptr创建(浮点f){
    vec.push_back(std::make_unique(entity{true,f}));
    返回vec.back();
    }
    无效刷新(){
    向量擦除(std::remove_if(vec.begin(),vec.end(),
    [](const auto&ent){return!ent->alive;}),
    vec.end());
    }
    空增长(大小n){vec.reserve(n);}
    void print(){//打印所有活动实体。
    用于(常数自动和输入:vec)
    
    std::在一个单词中输入数据,不。但是你可以有一个指针向量。
    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 << "Die\n"; // 5.5
        if (auto e = d2.lock()) std::cout << e->data << std::endl; else std::cout << "Die\n"; // 10.5
        if (auto e = d3.lock()) std::cout << e->data << std::endl; else std::cout << "Die\n"; // 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 << "Die\n"; // 5.5
        if (auto e = d2.lock()) std::cout << e->data << std::endl; else std::cout << "Die\n"; // Die
        if (auto e = d3.lock()) std::cout << e->data << std::endl; else std::cout << "Die\n"; // 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 << "Die\n"; // 5.5
        if (auto e = d2.lock()) std::cout << e->data << std::endl; else std::cout << "Die\n"; // Die
        if (auto e = d3.lock()) std::cout << e->data << std::endl; else std::cout << "Die\n"; // 6.5
    }