【问题标题】:How to add and remove object from vector<unique_ptr>...?如何从 vector<unique_ptr>... 添加和删除对象?
【发布时间】:2021-11-23 11:41:44
【问题描述】:

我有两个这样的功能。当我添加一个新对象时,一切都很好,但是如果我尝试添加一个已经在这个向量中的对象,应用程序就会关闭。从向量中删除后,我正在运行 unique_ptr 析构函数。如何解决?

void AddObject(GameObject* obj) {
    objects().push_back(std::move(unique_ptr<GameObject>(obj)));
}
        
void DeleteObject(GameObject* obj) {
    for (auto itr = objects().begin(); itr != objects().end(); ++itr) {
        if ((*itr)->ID == obj->ID) {
            objects().erase(std::move(itr));
            itr->~unique_ptr();
        }
    }
}

【问题讨论】:

  • I am running the unique_ptr deconstructor after removing from a vector. 为什么?而这一切是为了什么? std::move(unique_ptr&lt;GameObject&gt;(obj)) 使用emplace_back
  • @TedKleinBergman 很抱歉,我需要在具有超类类型的向量中使用子类的字段和方法。我发现并有效的第一种实现方法是使用 unique_ptr。
  • @tkausl objects().emplace_back(std::move(unique_ptr(obj)));没有帮助
  • objects().emplace_back(obj); .
  • But even if you just add the same object more than 2 times 好吧,你不应该有多个拥有同一个对象的unique_ptrs。

标签: c++ vector unique-ptr


【解决方案1】:

关于你显示的代码的所有内容都是错误的。

AddObject() 应该将unique_ptr&lt;GameObject&gt; 作为输入,而不是原始的GameObject* 指针,例如:

void AddObject(unique_ptr<GameObject> obj) {
    objects().push_back(std::move(obj));
}

否则,调用者可能会多次添加同一个对象,因此它将由多个unique_ptrs 管理。

更好的选择是不让调用者创建自己的对象。让调用者指定对象类型和构造函数参数,但让AddObject() 处理实际创建,例如:

template<typename T, typename... Args>
void AddObject(Args&&... args) {
    objects().push_back(std::make_unique<T>(std::forward<Args>(args)...));
}

至于DeleteObject()erase()从向量中获取元素使迭代器无效,这些迭代器引用了擦除点处或之后的元素,包括被擦除的迭代器时间>。你的循环没有考虑到这一点。 erase() 将迭代器返回到被擦除元素之后的元素,所以如果你不打算去break 你的循环(你可以有多个具有相同ID 值的对象吗?)那么你需要使用它iterator 继续正确循环。

另外,直接调用不是用placement-new 创建的对象的析构函数是未定义的行为。在这种情况下,根本没有理由手动销毁unique_ptrs。当它们从向量中移除时,它们会在超出范围时自动销毁。

如果您打算让DeleteObject() 搜索特定对象,那么它应该查找该特定对象指针,而不是 ID:

void DeleteObject(GameObject* obj) {
    auto &objs = objects();
    for (auto itr = objs.begin(); itr != objs.end(); ++itr) {
        if (itr->get() == obj) {
            objs.erase(itr);
            return;
        }
    }
}

或者:

void DeleteObject(GameObject* obj) {
    auto &objs = objects();
    auto itr = std::find_if(objs.begin(), objs.end(),
        [=](auto &ptr){ return ptr.get() == obj; }
    );
    if (itr != objs.end()) {
        objs.erase(itr);
    }
}

如果您打算让DeleteObject() 搜索 ID,那么它应该将 ID 作为输入,而不是对象指针:

void DeleteObject(int ID) {
    auto &objs = objects();
    for (auto itr = objs.begin(); itr != objs.end(); ++itr) {
        if ((*itr)->ID == ID) {
            itr = objs.erase(itr);
            return;
        }
    }
}

或者:

void DeleteObject(int ID) {
    auto &objs = objects();
    auto itr = std::find_if(objs.begin(), objs.end(),
        [=](auto &ptr){ return ptr->ID == ID; }
    );
    if (itr != objs.end()) {
        objs.erase(itr);
    }
}

或者,如果多个对象可以具有相同的 ID (?):

void DeleteObject(int ID) {
    auto &objs = objects();
    for (auto itr = objs.begin(); itr != objs.end(); ) {
        if ((*itr)->ID == ID) {
            itr = objs.erase(itr);
        }
        else {
            ++itr;
        }
    }
}

或者:

void DeleteObject(int ID) {
    auto &objs = objects();
    objs.erase(
        std::remove_if(objs.begin(), objs.end(),
            [=](auto &ptr){ return ptr->ID == ID; }
        ),
        objs.end()
    );

    // or, in C++20 and later:

    std::erase_if(objs,
        [=](auto &ptr){ return ptr->ID == ID; }
    );
}

【讨论】:

    猜你喜欢
    • 2023-03-04
    • 2013-07-11
    • 2017-10-27
    • 1970-01-01
    • 2021-11-26
    • 2017-11-30
    • 2020-12-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多