2015-10-18 180 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