为什么std :: unique_ptr没有像std :: shared_ptr这样的别名构造函数?

时间:2016-01-08 03:11:37

标签: c++ c++11

我刚刚发现aws dynamodb get-item --table-name users --key file://test.json 的“别名构造函数”并发现自己在问“为什么std :: unique_ptr没有相应的?”

也就是说,如果你想分配一个std::shared_ptr,以便你可以将Foo成员传递给一个应该完全管理Bar生命周期的函数,那不是吗很高兴能够这样做吗?

Foo

这适用于std :: shared_ptr(http://ideone.com/pDK1bc

#include <memory>

struct B {}
struct A {
  B b;
}

void f(std::unique_ptr<B> b);

std::unique_ptr<A> a = std::make_unique<A>();
std::unique_ptr<B> b { std::move(a), &(a->b) };  // a now invalid.
f(std::move(b));  // f now responsible for deleting the A.

输出预期的

#include <iostream>
#include <memory>
#include <string>

struct B {
  std::string s;
};
struct A {
  B b;
  A(std::string s) : b{s} {};
  ~A() { std::cout << "A deleted." << std::endl; }
};

void f(std::shared_ptr<B> b) {
  std::cout << "in f, b->s = " << b->s << " (use_count=" << b.use_count() << ")" << std::endl;
}

int main() {
  std::shared_ptr<A> a = std::make_shared<A>("hello");
  std::shared_ptr<B> b { a, &(a->b) };
  a.reset();  // a now invalid.
  std::cout << "before f, b->s = " << b->s << " (use_count=" << b.use_count() << ")" << std::endl;
  f(std::move(b));  // f now responsible for deleting the A.
  std::cout << "after f" << std::endl;
  return 0;
}

有没有合理的理由说明为什么没有包含这样的东西?和/或,使用带有删除before f, b->s = hello (use_count=1) in f, b->s = hello (use_count=1) A deleted. after f 的自定义删除工具unique_ptr<B>模拟它是不是一个坏主意?

2 个答案:

答案 0 :(得分:8)

我认为“问题”在于,与std::shared_ptr不同,std::unique_ptr的删除器不会被类型删除。 std::unique_ptr<T>的默认删除(其大小为零,编码为类型本身作为几乎不可见的默认类型参数)只是[](T * p){ delete p; }。但很明显,通过std::unique_ptr<B>创建的std::make_unique<B>和通过指向B对象的A成员创建的A *不能拥有相同的删除者。后一种情况的删除器必须进行一些指针运算才能获得原始的std::unique_ptr指针。如果两个删除器都存储偏移量或内部指针指向原始对象,那么这两个删除器只能具有相同的类型。那将不再是零大小。与手动执行newdelete相比,{{1}}的开销设计为零,这是一件好事。我没有看到使用你自己的删除器存储额外指针的任何直接缺点,尽管我仍然需要遇到一个用例,我发现它很有用。

答案 1 :(得分:5)

shared_ptr有引用计数开销。在它的引用计数块中,它还存储一个显式的删除器(因为如果你在堆上存储,还有几个字节?)

这也是为什么基类型的shared_ptr可以记住在没有虚拟dtor的情况下删除派生类型的原因。

另一方面,

unique_ptr将其删除存储在实例中,默认删除器是无状态的 - 使用0个字节。这使得unique_ptr在内存使用方面比原始指针零开销。

无状态删除者不记得删除别的东西。

您可以向支持别名的unique_ptr添加有状态删除器,但之后您必须手动设置别名。其中一个构造函数同时使用指针和删除器。