2016-01-08 88 views
5

我刚刚发现std::shared_ptr的‘走样构造’,找到自己问“为什么不标准::的unique_ptr有相应的一个?为什么不std :: unique_ptr具有像std :: shared_ptr一样的别名构造函数?

,如果你想分配一个Foo这样就可以了,也就是其Bar成员传递给应完全管理Foo寿命的功能,那岂不是很高兴能够这样做呢?

#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. 

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

#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>与定制的删除程序来删除A来模拟它是不是一个好主意?

回答

8

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

5

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

这也是为什么一个基类型的shared_ptr可以记住删除派生类型一个虚拟的dtor。

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

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

您可以将有状态的删除程序添加到支持别名的unique_ptr,但您必须手动创建别名。其中一个构造函数同时使用指针和删除器。

+0

然后,由于删除者的类型部分,除非您键入删除删除者,否则它不会与纯粹的'unique_ptr '很好地互操作,这更加额外的开销。 –

+0

@ T.C .:即使您键入删除删除程序,它也不会与'unique_ptr >'互操作。至少,只能在一个方向上进行转换。 –

+0

@理论上讲,只要失败是允许的。 – Yakk

相关问题