c++11 智能指针 (std::unique_ptr)(一)

定义于头文件 <memory>
template<class T,class Deleter = std::default_delete<T>> 
class unique_ptr; (1) (C++11 起) 

template <class T, class Deleter> 
class unique_ptr<T[], Deleter>; (2) (C++11 起) 

std::unique_ptr 是通过指针占有并管理另一对象,并在 unique_ptr 离开作用域时释放该对象的智能指针。

在下列两者之一发生时用关联的删除器释放对象:

  • 销毁了管理的 unique_ptr 对象
  • 通过 operator= 或 reset() 赋值另一指针给管理的 unique_ptr 对象。

通过调用 get_deleter()(ptr) ,用潜在为用户提供的删除器释放对象。默认删除器用 delete 运算符,它销毁对象并解分配内存。

unique_ptr 亦可以不占有对象,该情况下称它为空 (empty)

std::unique_ptr 有两个版本:

1) 管理单个对象(例如以 new 分配)

2) 管理动态分配的对象数组(例如以 new[] 分配)

类满足可移动构造 (MoveConstructible) 和可移动赋值 (MoveAssignable) 的要求,但不满足可复制构造 (CopyConstructible) 或可复制赋值 (CopyAssignable) 的要求。

类型要求
- Deleter 必须是函数对象 (FunctionObject) 或到函数对象 (FunctionObject) 的左值引用或到函数的左值引用,可以 unique_ptr<T, Deleter>::pointer 类型参数调用

注意

只有非 const 的 unique_ptr 能转移被管理对象的所有权给另一 unique_ptr 。若对象的生存期为 const std::unique_ptr 所管理,则它被限定在创建指针的作用域中。

std::unique_ptr 常用于管理对象的生存期,包含:

  • 通过正常退出和经由异常退出两者上的受保证删除,提供异常安全,给处理拥有动态生存期的对象的类和函数
  • 传递独占的拥有动态生存期的对象的所有权到函数
  • 从函数获得独占的拥有动态生存期对象的所有权
  • 作为具移动容器的元素类型,例如保有指向动态分配对象的指针的 std::vector (例如,若想要多态行为)

std::unique_ptr 可为不完整类型 T 构造,例如用于改善用作 pImpl 手法中把柄的用途。若使用默认删除器,则 T 必须在代码中调用删除器点处完整,这发生于析构函数、移动赋值运算符和 std::unique_ptrreset 成员函数中。(相反地, std::shared_ptr 不能从指向不完整类型的裸指针构造,但可于 T 不完整处销毁)。注意若 T 是类模板特化,则以 unique_ptr 为运算数的使用,如 !p ,因 ADL 而要求 T 的形参完整。

T 是某基类 B 的导出类,则 std::unique_ptr<T> 可隐式转换为 std::unique_ptr<B>。产生的 std::unique_ptr<B> 的默认删除器将使用 B 的 operator delete ,这导致未定义行为,除非 B 的析构函数为虚。注意 std::shared_ptr 表现有别: std::shared_ptr<B> 将使用类型 T 的 operator delete ,而且即使 B 的析构函数非虚,也会正确删除被占有对象。

不同于 std::shared_ptr , std::unique_ptr 可通过任何满足可空指针 (NullablePointer) 的定制把柄类型管理对象。例如,这允许管理位于共享内存,但提供定义 typedef boost::offset_ptr pointer; 或其他缀饰指针的 Deleter 的对象。

构造函数

初等模板 unique_ptr<T> 的成员

constexpr unique_ptr() noexcept;
constexpr unique_ptr( nullptr_t ) noexcept;        (1)  

explicit unique_ptr( pointer p ) noexcept;         (2)  

unique_ptr( pointer p, /* see below */ d1 ) noexcept; (3)  

unique_ptr( pointer p, /* see below */ d2 ) noexcept; (4)  

unique_ptr( unique_ptr&& u ) noexcept;                (5)  

template< class U, class E >
unique_ptr( unique_ptr<U, E>&& u ) noexcept;          (6)  

template< class U >
unique_ptr( std::auto_ptr<U>&& u ) noexcept;          (7) (C++17 中移除) 

数组特化 unique_ptr<T[]> 的成员

constexpr unique_ptr() noexcept;
constexpr unique_ptr( nullptr_t ) noexcept;         (1)  

explicit unique_ptr( pointer p ) noexcept;          (2) (C++17 前) 

template<class U> explicit unique_ptr( U p ) noexcept; (2) (C++17 起) 

unique_ptr( pointer p, /* see below */ d1 ) noexcept;  (3) (C++17 前) 

template<class U> unique_ptr( U p, /* see below */ d1 ) noexcept; (3) (C++17 起) 

unique_ptr( pointer p, /* see below */ d2 ) noexcept;  (4) (C++17 前) 

template<class U> unique_ptr( U p, /* see below */ d2 ) noexcept;(4) (C++17 起) 

unique_ptr( unique_ptr&& u ) noexcept; (5)  

template< class U, class E >
unique_ptr( unique_ptr<U, E>&& u ) noexcept; (6) (C++17 起) 

1) 构造不占有对象的 std::unique_ptr 。值初始化存储的指针和存储的删除器。要求 Deleter 可默认构造 (DefaultConstructible) 且构造不抛异常。

Deleter 是指针或引用类型则此构造函数为病式。

(C++17 前)

这些重载仅若 std::is_default_constructible<Deleter>::value 为 true 且 Deleter 不是指针类型才参与重载决议。

(C++17 起)

2) 构造占有 p 的 std::unique_ptr ,以 p 初始化存储的指针,并值初始化存储的删除器。要求 Deleter 可默认构造 (DefaultConstructible) 且构造不抛异常。

Deleter 是指针或引用类型则此构造函数为病式。

(C++17 前)

此重载仅若 std::is_default_constructible<Deleter>::value 为 true 且 Deleter 不是指针类型才参与重载决议。若类模板实参推导选择此构造函数,则程序为病式。

(C++17 起)

3-4) 构造占有 pstd::unique_ptr 对象,以 p 初始化存储的指针,并按下列方式初始化删除器 D (依赖于 D 是否为引用类型)

a) 若 D 是非引用类型 A ,则签名是:

unique_ptr(pointer p, const A& d) noexcept;

(1)(要求 Deleter 为不抛出可复制构造 (CopyConstructible) )

unique_ptr(pointer p, A&& d) noexcept;

(2)(要求 Deleter 为不抛出可移动构造 (MoveConstructible) )

b) 若 D 是左值引用类型 A& ,则签名是:

unique_ptr(pointer p, A& d) noexcept;

(1)

unique_ptr(pointer p, A&& d);

(2)

c) 若 D 是左值引用类型 const A& ,则签名是:

unique_ptr(pointer p, const A& d) noexcept;

(1)

unique_ptr(pointer p, const A&& d);

(2)

所有情况中删除器从 std::forward<decltype(d)>(d) 初始化。

D 是引用类型且选择第二重载,则程序为病式。

(C++17 前)

D 是引用类型,则定义第二重载为被删除。这些重载仅若 std::is_constructible<D, decltype(d)>::value 为 true 才参与重载决议。 若类模板时实参推导选择二个构造函数之一,则程序为病式。

(C++17 起)
2-4) 数组特化中表现同初等模板中接收一个指针参数的构造函数,除了它们不另外参与重载决议,除非下列之一为真:
  • Upointer 为同一类型,或
  • U 为 std::nullptr_t ,或
  • pointerelement_type* 为同一类型且 U 为指针类型 V* ,满足 V(*)[] 可隐式转换为 element_type(*)[]
(C++17 起)

5) 通过从 u 转移所有权给 *this 构造 unique_ptr 并存储空指针于 u 。此构造函数仅若 std::is_move_constructible<Deleter>::value 为 true 才参与重载决议。若 Deleter 不是引用类型,则要求它为不抛出可移动构造 (MoveConstructible) (若 Deleter 是引用,则 get_deleter()u.get_deleter() 在移动构造后引用相同值)。

6) 通过从 u 转移所有权给 *this 构造 unique_ptr ,其中 u 以指定的删除器( E )构造。它依赖于 E 是否引用类型,如下:

a) 若 E 是引用类型,则从 u 的删除器复制构造此删除器(要求此构造不抛出)

b) 若 E 不是引用类型,则从 u 的删除器移动构造此删除器(要求此构造不抛出)

此构造函数仅若下列皆为真才参与重载决议:

a) unique_ptr<U, E>::pointer 可隐式转换为 pointer

b) U 不是数组类型

c) Deleter 是引用类型且 ED 为同一类型,或 Deleter 不是引用类型且 E 可隐式转换为 D

6) 数组的特化中,表现同在初等模板中者,除了它仅若下列皆为真才会参与重载决议
  • U 是数组类型
  • pointerelement_type* 是同一类型
  • unique_ptr<U,E>::pointer 与 unique_ptr<U,E>::element_type* 是同一类型
  • unique_ptr<U,E>::element_type(*)[] 可转换为 element_type(*)[]
  • Deleter 是引用类型且 EDeleter 是同一类型,或 Deleter 非引用类型且 E 可隐式转换为 Deleter
(C++17 起)

7) 构造 unique_ptr ,其中以 u.release() 初始化存储的指针,并值初始化存储的删除器。此构造函数仅若 U* 可隐式转换为 T*Deleterstd::default_delete<T> 为同一类型才参与重载决议。

参数

p-指向要管理的对象的指针
d1,d2-用于销毁对象的删除器
u-获取所有权来源的智能指针

注意

使用 std::make_unique<T> 是更好的做法,可取代与 new 一起的重载 (2) 。

std::unique_ptr<Derived> 通过重载 (6) 可隐式转换为 std::unique_ptr<Base> (因为被管理指针和 std::default_delete 都可隐式转换)。

因为默认构造函数是 constexpr ,故静态的 unique_ptr 作为静态非局部初始化的一部分初始化,在任何动态初始化之前。这使得在任何静态对象的构造函数中使用 unique_ptr 是安全的。

无源自指针类型的类模板实参推导,因为不可能辨别来自 new 的数组和非数组形式的指针。

(C++17 起)

调用示例 

#include <iostream>
#include <memory>

struct Foo   // 要管理的对象
{
    Foo()
    {
        std::cout << "Foo ctor\n";
    }
    Foo(const Foo&)
    {
        std::cout << "Foo copy ctor\n";
    }
    Foo(Foo&&)
    {
        std::cout << "Foo move ctor\n";
    }
    ~Foo()
    {
        std::cout << "~Foo dtor\n";
    }
};

struct D   // 删除器
{
    D() {};
    D(const D&)
    {
        std::cout << "D copy ctor\n";
    }
    D(D&)
    {
        std::cout << "D non-const copy ctor\n";
    }
    D(D&&)
    {
        std::cout << "D move ctor \n";
    }
    void operator()(Foo* p) const
    {
        std::cout << "D is deleting a Foo\n";
        delete p;
    };
};

int main()
{
    std::cout << "Example constructor(1)...\n";
    std::unique_ptr<Foo> up1;  // up1 为空
    std::unique_ptr<Foo> up1b(nullptr);  // up1b 为空

    std::cout << "Example constructor(2)...\n";
    {
        std::unique_ptr<Foo> up2(new Foo); // up2 现在占有 Foo
    } // Foo 被删除

    std::cout << "Example constructor(3)...\n";
    D d;
    {
        // 删除器类型不是引用
        std::unique_ptr<Foo, D> up3(new Foo, d); // 复制删除器
    }
    {
        // 删除器类型是引用
        std::unique_ptr<Foo, D&> up3b(new Foo, d); // up3b 保有到 d 的引用
    }

    std::cout << "Example constructor(4)...\n";
    {
        // 删除器不是引用
        std::unique_ptr<Foo, D> up4(new Foo, D()); // 移动删除器
    }

    std::cout << "Example constructor(5)...\n";
    {
        std::unique_ptr<Foo> up5a(new Foo);
        std::unique_ptr<Foo> up5b(std::move(up5a)); // 所有权转移
    }

    std::cout << "Example constructor(6)...\n";
    {
        std::unique_ptr<Foo, D> up6a(new Foo, d); // 复制 D
        std::unique_ptr<Foo, D> up6b(std::move(up6a)); // 移动 D

        std::unique_ptr<Foo, D&> up6c(new Foo, d); // D 是引用
        std::unique_ptr<Foo, D> up6d(std::move(up6c)); // 复制 D
    }

    std::cout << "Example constructor(7)...\n";
    {
        std::auto_ptr<Foo> up7a(new Foo);
        std::unique_ptr<Foo> up7b(std::move(up7a)); // 所有权转移
    }

    std::cout << "Example array constructor...\n";
    {
        std::unique_ptr<Foo[]> up(new Foo[3]);
    } // 删除三个 Foo 对象
}

输出

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值