stl容器与std :: unique_ptr的vs boost :: ptr_container

P3t*_*rus 19 c++ stl unique-ptr boost-ptr-container c++11

有了c ++ 11,我问自己是否在c ++ 11中替换了boost :: ptr_containers.我知道我可以使用例如a std::vector<std::unique_ptr<T> >,但我不确定这是否完全替代.处理这些案件的推荐方法是什么?

How*_*ant 22

我决定编写一个简短的程序,将一些多态对象放入容器(通过指向堆的指针),然后将该容器与std :: algorithm一起使用.我选择的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();
}
Run Code Online (Sandbox Code Playgroud)

这输出:

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
Run Code Online (Sandbox Code Playgroud)

这对我来说很好看.但是我发现将其转化为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.
Run Code Online (Sandbox Code Playgroud)

问题是以下特性boost::ptr_vector:迭代器不返回内部存储的指针.他们返回被解除引用的指针.因此,当容器与之一起使用时std::algorithms,算法会尝试复制存储的对象而不是存储的指向对象的指针.

如果有人意外忘记使您的多态对象不可复制,则会自动提供复制语义,从而导致运行时错误而不是编译时错误:

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

    virtual void speak() const = 0;
};
Run Code Online (Sandbox Code Playgroud)

现在导致这个错误的输出:

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
Run Code Online (Sandbox Code Playgroud)

使用时不会发生此运行时错误vector<unique_ptr>.

存储指针容器但呈现参考容器的阻抗不匹配似乎与使用通用算法的容器的安全使用不一致.实际上,这就是为什么ptr_containers带有许多算法的自定义版本.使用ptr_containers执行此任务的正确方法是使用那些成员算法:

v.erase_if([](Animal& p)
                 {return dynamic_cast<Sheep*>(&p);});
Run Code Online (Sandbox Code Playgroud)

如果您需要一个不作为ptr_containers成员提供的变异序列算法,请不要试图接触那些<algorithm>或其他第三方提供的通用算法.

总之,当唯一的其他实用选项是时,boost :: ptr_containers填补了真正的需求std::vector<boost::shared_ptr<T>>.但是现在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工作,调试过的代码,就没有迫切需要改变它.


Nic*_*las 20

他们真的解决了两个相似但不同的问题.

指针容器是一种在容器中存储对象的方法,恰好恰好是分配内存而不是值的指针.他们竭尽全力隐藏他们是指针容器的事实.这意味着:

  • 容器中的条目不能为NULL.
  • 从迭代器和函数获得的值是类型的引用,而不是对类型的指针.
  • 使用许多标准算法可能会很棘手.而"狡猾",我的意思是破碎.指针容器有自己的内置算法.

但是,指针容器知道它们是指针的容器,它们可以提供一些新功能:

  • clone执行深层副本,经由在对象的类型中使用了一定的"Cloneable的"概念的成员函数.
  • 容器释放其对象所有权的能力(例如,在浅拷贝之后).
  • 内置函数将所有权转移到其他容器.

他们真的是完全不同的概念.你需要手动完成很多东西,指针容器可以通过专门的函数自动完成.

如果你真的需要一个指针容器,那么你可以使用容器unique_ptr.但是如果你需要存储一堆碰巧堆积的对象,并且想要与它们一起玩特殊游戏,那么指针容器并不是一个坏主意.