通过属性值从向量中删除对象的 unique_ptr

Gov*_*van 5 c++ smart-pointers vector unique-ptr c++11

我有一个具有这三个功能的 Holder 对象

unique_ptr<Object> Holder::remove(string objName){
    std::vector<unique_ptr<Object>>::iterator object = 
            find_if(objects.begin(), objects.end(),
            [&](unique_ptr<Object> & obj){ return obj->name() == objName;}
     );
    objects.erase(std::remove(objects.begin(), objects.end(), *object));
    return std::move(*object);
}

vector<unique_ptr<Object>> const& Holder::getContent() const {
    return this->objects;
}

void Holder::add(unique_ptr<Object> objPtr) {
    this->objects.push_back(move(objPtr));
}
Run Code Online (Sandbox Code Playgroud)

我写了一个 CPPunit 测试如下:

void HolderTest::removeObject() {
    Holder holder("bag");
    unique_ptr<Object> ringPtr(new Object("a"));
    holder.add(move(ringPtr));

    unique_ptr<Object> swordPtr(new Object("b"));
    holder.add(move(swordPtr));

    holder.remove("a");
    vector<unique_ptr<Object>> const& objects = holder.getContent();
    CPPUNIT_ASSERT(objects.size() == 1);
}
Run Code Online (Sandbox Code Playgroud)

这个测试毫无问题地通过,但对我来说很奇怪的是,如果我添加以下行:

const std::string name = objects[0].get()->name();
CPPUNIT_ASSERT_EQUALS("b", name);
Run Code Online (Sandbox Code Playgroud)

然后测试在没有任何消息的情况下崩溃。我在另一个测试中编写了这一行而没有调用 remove 并且它没有任何问题。如果我将向量大小的值更改为 2 或 0 CPPUNIT_ASSERT(objects.size() == 2); 然后测试失败。所以看起来remove函数保留了unique_ptr之一,但它把它变成了一个nullptr?有什么问题吗?

seh*_*ehe 5

    std::vector<unique_ptr<Object>>::iterator object = 
        find_if(objects.begin(), objects.end(),
                [&](unique_ptr<Object> & obj){ return obj->name() == objName;}
               );
    objects.erase(std::remove(objects.begin(), objects.end(), *object));
    return std::move(*object);
Run Code Online (Sandbox Code Playgroud)

在迭代器失效object 取消对它的引用。见迭代器失效规则

在擦除之前移动指针,然后你会没事的。

其他注意事项:

  • removing与值一起使用很有趣(而不仅仅是删除您获得的迭代器)。您是否希望向量包含重复项?实际上,请注意:那将是erase错误的,因为它总是删除一个元素
  • 在取消引用之前,您也不检查它object可能是end()迭代器。未定义行为的另一个来源
  • 考虑name通过const&提高效率

Live On Coliru

#include <memory>
#include <vector>
#include <iostream>
#include <algorithm>

using namespace std;

struct Object {
    Object(std::string name) : _name(std::move(name)) { }

    std::string const& name() const { return _name; }
  private:
    std::string _name;
};

struct Holder {
    using Ptr = unique_ptr<Object>;

    Ptr remove(string const& objName) {

        auto it = find_if(objects.begin(), objects.end(), [&](Ptr& obj){ return obj->name() == objName; });

        if (it != objects.end()) {
            auto retval = std::move(*it);
            objects.erase(it);
            return std::move(retval);
        }

        return {}; // or handle as error?
    }

    vector<Ptr> const& getContent() const {
        return this->objects;
    }

    void add(Ptr objPtr) {
        this->objects.push_back(move(objPtr));
    }

  private:
    vector<Ptr> objects;
};

int main() {

    Holder h;
    for(auto n: { "aap", "noot", "mies", "broer", "zus", "jet" })
        h.add(std::make_unique<Object>(n));

    h.remove("broer");
    h.remove("zus");

    for (auto& o : h.getContent())
        std::cout << o->name() << "\n";
}
Run Code Online (Sandbox Code Playgroud)

印刷

aap
noot
mies
jet
Run Code Online (Sandbox Code Playgroud)