2012-02-27 194 views
15

隨着c + + 11在那裏,我問自己是否有一個替代boost :: ptr_containers在c + + 11。我知道我可以使用例如一個std::vector<std::unique_ptr<T> >,但我不確定這是否是一個完整的替代品。處理這些案件的推薦方式是什麼?stl容器與std :: unique_ptr的vs boost :: ptr_container

+2

對於'unique_ptr',你仍然需要對這些節點進行解引用,但除此之外它們應該幾乎表現相同。 – 2012-02-27 18:12:18

回答

19

他們真的解決了兩個相似但不同的問題。

指針容器是一種將對象存儲在容器中的方式,它恰好是分配內存而不是值的指針。他們盡其所能,以hide事實上,他們是一個指針的容器。這意味着:

  • 容器中的條目不能爲NULL。
  • 從迭代器和函數獲得的值是引用的類型,而不是指向類型的指針。
  • 使用許多標準算法可能會很棘手。而「棘手」,我的意思是破碎。指針容器有自己的內置算法。

然而,事實證明指針容器知道他們是指針的容器,它們可以提供一些新的功能:

  • 執行深拷貝,通過使用clone成員函數在對象的類型上具有某種「可克隆」概念。
  • 容器釋放對象所有權的能力(例如,在淺拷貝之後)。
  • 將所有權轉移給其他容器的內置函數。

他們真的是完全不同的概念。有很多東西你必須手動做,指針容器可以自動執行特定的功能。

如果你真的需要一個容器指針,那麼你可以使用容器unique_ptr。但是,如果你需要存儲一堆你碰巧分配的對象,並且你想要與它們一起玩特定的遊戲,包括所有權等,那麼指針容器並不是一個壞主意。

+1

我想你可以說他們中的一個明確地是指針的容器,而另一個是用於多態對象的容器...... – Mehrdad 2015-04-25 20:17:19

22

我決定編寫一個簡短的程序,將幾個多態對象放入一個容器(通過指向堆的指針),然後將該容器與std ::算法一起使用。我僅選擇std::remove_if作爲示例。

這裏是我如何與vector<unique_ptr<T>>做到這一點:

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

class Animal 
{ 
public: 
    Animal() = default; 
    Animal(const Animal&) = delete; 
    Animal& operator=(const Animal&) = delete; 
    virtual ~Animal() = default; 

    virtual void speak() const = 0; 
}; 

class Cat 
    : public Animal 
{ 
public: 
    virtual void speak() const {std::cout << "Meow\n";} 
    virtual ~Cat() {std::cout << "destruct Cat\n";} 
}; 

class Dog 
    : public Animal 
{ 
public: 
    virtual void speak() const {std::cout << "Bark\n";} 
    virtual ~Dog() {std::cout << "destruct Dog\n";} 
}; 

class Sheep 
    : public Animal 
{ 
public: 
    virtual void speak() const {std::cout << "Baa\n";} 
    virtual ~Sheep() {std::cout << "destruct Sheep\n";} 
}; 

int main() 
{ 
    typedef std::unique_ptr<Animal> Ptr; 
    std::vector<Ptr> v; 
    v.push_back(Ptr(new Cat)); 
    v.push_back(Ptr(new Sheep)); 
    v.push_back(Ptr(new Dog)); 
    v.push_back(Ptr(new Sheep)); 
    v.push_back(Ptr(new Cat)); 
    v.push_back(Ptr(new Dog)); 
    for (auto const& p : v) 
     p->speak(); 
    std::cout << "Remove all sheep\n"; 
    v.erase(
     std::remove_if(v.begin(), v.end(), 
         [](Ptr& p) 
          {return dynamic_cast<Sheep*>(p.get());}), 
     v.end()); 
    for (auto const& p : v) 
     p->speak(); 
} 

此輸出:

Meow 
Baa 
Bark 
Baa 
Meow 
Bark 
Remove all sheep 
destruct Sheep 
destruct Sheep 
Meow 
Bark 
Meow 
Bark 
destruct Dog 
destruct Cat 
destruct Dog 
destruct Cat 

這對我來說很好。但是我發現翻譯這ptr_vector問題:

boost::ptr_vector<Animal> v; 
v.push_back(new Cat); 
v.push_back(new Sheep); 
v.push_back(new Dog); 
v.push_back(new Sheep); 
v.push_back(new Cat); 
v.push_back(new Dog); 
for (auto const& p : v) 
    p.speak(); 
std::cout << "Remove all sheep\n"; 
v.erase(
    std::remove_if(v.begin(), v.end(), 
        [](Animal& p) 
         {return dynamic_cast<Sheep*>(&p);}), 
    v.end()); 
for (auto const& p : v) 
    p.speak(); 

algorithm:1897:26: error: overload resolution selected deleted operator '=' 
       *__first = _VSTD::move(*__i); 
       ~~~~~~~~^~~~~~~~~~~~~~~~~~ 
test.cpp:75:9: note: in instantiation of function template specialization 'std::__1::remove_if<boost::void_ptr_iterator<std::__1::__wrap_iter<void 
     **>, Animal>, Sheep *(^)(Animal &)>' requested here 
     std::remove_if(v.begin(), v.end(), 
     ^
test.cpp:12:13: note: candidate function has been explicitly deleted 
    Animal& operator=(const Animal&) = delete; 
      ^
1 error generated. 

的問題是boost::ptr_vector一個特點:迭代器不返回內部存儲的指針。他們返回解除引用的指針。因此,當容器與std::algorithms一起使用時,算法嘗試將存儲的對象而不是存儲的指針複製到對象。

如果一個不小心忘了讓你的多態對象不可複製,再複製語義自動供應,導致運行時錯誤而不是編譯時錯誤:

class Animal 
{ 
public: 
    Animal() = default; 
    virtual ~Animal() = default; 

    virtual void speak() const = 0; 
}; 

現在造成這個錯誤輸出:

Meow 
Baa 
Bark 
Baa 
Meow 
Bark 
Remove all sheep 
destruct Cat 
destruct Dog 
Meow 
Baa 
Bark 
Baa 
destruct Cat 
destruct Sheep 
destruct Dog 
destruct Sheep 

使用vector<unique_ptr>時不會發生此運行時錯誤。

存儲指針容器但顯示引用容器的阻抗不匹配與通用算法安全使用容器不一致。實際上,這就是爲什麼ptr_containers帶有許多算法的定製版本。做好這項工作與ptr_containers正確的方法是使用那些成員算法:

v.erase_if([](Animal& p) 
       {return dynamic_cast<Sheep*>(&p);}); 

如果您不需要提供作爲ptr_containers成員的突變序列算法,不要試圖去夠那些在<algorithm>中,或其他第三方提供的通用算法。

總之,當唯一的其他實用選項是std::vector<boost::shared_ptr<T>>時,boost :: ptr_containers滿足了真正的需求。但現在std::vector<std::unique_ptr<T>>,開銷參數消失了。 C++ 11解決方案似乎具有安全性和靈活性兩方面的優勢。如果您需要「克隆語義」,我會認真考慮編寫您自己的clone_ptr<T>,並將其用於std容器和算法。

重複使用std :: lib會使容器的選項比boost lib更加開放(例如unordered_set/map,forward_list),並且它會使std :: algorithms的選項保持儘可能寬。這就是說,如果你有工作,調試過的代碼已經使用boost :: ptr_containers,那麼沒有迫切需要改變它。

+0

「開銷參數不見了」 - 今天,我用VS2013-Express做了一些測試令人驚訝的是,我通過'ptr_vector'獲得了更好的性能結果,而不是'vector '(/ O2 release build; boost 1.55)。這個評論帶有一個明確的YMMV,我還沒有對此進行深入研究,但是如果一個人在非常狹小的空間裏表現明智,看看兩者都可能會很有趣。 – 2014-01-15 22:59:31

+0

有趣,感謝報告。我沒有VS2013進行試驗。是'sizeof(unique_ptr )== sizeof(T *)'?/O2是最高優化設置嗎? – 2014-01-16 00:00:19

+0

是的,sizeof(uq_ptr)== sizeof(T *)== 4./O2是VS中「最大化速度」。在索引'[]'訪問時,我看到ptr_vector有50%的速度增益。 – 2014-01-16 19:05:11

相關問題