在STL容器中使用unique_ptr什么时候有意义?(C++11)



unique_ptr的容器似乎没有什么意义:您不能将它与初始值设定项列表一起使用,并且我未能迭代该容器(解决方法如下)。我是不是误解了什么?或者什么时候使用unique_ptr和STL容器有意义?

#include <memory>
#include <vector>
using namespace std;
struct Base { void go() { }  virtual ~Base() { } }; 
// virtual ~Base() = default; gives
// "declared virtual cannot be defaulted in the class body" why?
class Derived : public Base { };
int main() {
  //vector<unique_ptr<Base>> v1 = { new Derived, new Derived, new Derived };
  //vector<shared_ptr<Base>> v2 = { new Derived, new Derived, new Derived };
  vector<Base*> v3 = { new Derived, new Derived, new Derived };
  vector<shared_ptr<Base>> v4(v3.begin(), v3.end());
  vector<unique_ptr<Base>> v5(v3.begin(), v3.end());
  for (auto i : v5) { // works with v4
    i->go();
  }
  return 0;
}


以下问题帮助我找到了这些解决方法:

  • 如何用初始化器列表初始化不可压缩的容器?

  • 我可以列表初始化只移动类型的矢量吗?

  • 当我需要NoCopy类型的容器时,我通常使用boost::ptr_vectorstd::vector<shared_ptr>

for (auto i : v5) {
  i->go();
}

应该是

for (auto& i : v5) { // note 'auto&'
  i->go();
}

否则,您将尝试复制当前元素。

此外,不能使用这样的初始值设定项列表,因为std::unique_ptrstd::shared_ptr的构造函数标记为explicit。你需要做这样的事情:

#include <iterator> // make_move_iterator, begin, end
template<class T>
std::unique_ptr<T> make_unique(){ // naive implementation
  return std::unique_ptr<T>(new T());
}
std::unique_ptr<Base> v1_init_arr[] = {
    make_unique<Derived>(), make_unique<Derived>(), make_unique<Derived>()
};
// these two are only for clarity
auto first = std::make_move_iterator(std::begin(v1_init_arr));
auto last = std::make_move_iterator(std::end(v1_init_arr));
std::vector<std::unique_ptr<Base>> v1(first, last);
std::vector<std::shared_ptr<Base>> v2 = {
    std::make_shared<Derived>(),
    std::make_shared<Derived>(),
    std::make_shared<Derived>()
};

这是一件好事™,因为否则可能会泄漏内存(如果后面的构造函数之一抛出,那么前面的构造函数还没有绑定到智能指针)。unique_ptr的注意事项是必要的,因为初始值设定项列表会复制它们的参数,而且由于unique_ptr不可复制,所以您会遇到问题。


也就是说,在我的一个项目中,我使用std::map<std::string, std::unique_ptr<LoaderBase>>作为加载器的字典。

您实际上可以使用std::unique_ptr<T>在容器中进行迭代而不会出现问题。。。您只需要访问唯一指针的引用(即,不是副本),或者实际需要对容器使用迭代器类型。在您的情况下,这将类似于vector<unique_ptr<Base>>::iteratorvector<unique_ptr<Base>>::const_iterator

unique_ptr在STL容器中包含无法复制的对象时是有意义的。或者,如果复制它们成本高昂或根本不正确。

您可以获得与相同的功能

vector<Base*> v3 = { new Derived, new Derived, new Derived };

但如果没有v3所吸引的内存泄漏。

最新更新