2015-10-18 86 views
2

我有这三个功能通过属性值

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)); 
} 

我已经写了如下一个CppUnit的测试一个Holder对象中除去来自矢量的对象的的unique_ptr:

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); 
} 

该测试是通过没有问题,但对我来说很奇怪的是,如果我加入下面的行:

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

然后测试崩溃,没有任何消息。我在另一个测试中写了这条线,没有调用remove,并且它没有任何问题。 如果我将矢量大小的值更改为2或0 CPPUNIT_ASSERT(objects.size()== 2); 然后测试失败。所以看来,删除功能是保持其中一个unique_ptr,但它把它变成一个nullptr? 任何iea有什么问题?

+0

你的迭代器 “对象” 是在此之后行无效:objects.erase(std :: remove(objects.begin(),objects.end(),* object));这使得该行“返回std :: move(* object);”导致未定义的行为和搞乱一切。 – Gene

回答

1
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); 

你反引用迭代器object后,它已经失效。请参阅Iterator invalidation rules

在擦除前移动指针,那么你会没事的。

其他说明:

  • 这很有趣使用removing与价值(而不是仅仅删除你得到了迭代器)。你期望矢量包含重复吗?其实,罢工:那会使erase错误,因为它总是删除一个元素
  • 你也不会检查object可能是在解引用前迭代器的end()。的Undefined Behaviour
  • 另一个来源考虑const&采取name效率

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"; 
} 

打印

aap 
noot 
mies 
jet 
+0

我没有真正明白你的意思?你的意思是我应该把std :: move(* object)赋给一个变量,然后我应该在返回中返回那个变量吗? – Govan

+0

@Govan我已经添加了一个完整的自包含示例,可以帮助您理解 – sehe

+0

谢谢!现在我遇到了问题。 – Govan