C++ 11新特性之unique_ptr

简介: C++ 11新特性之unique_ptr

image.png

概述

在C++ 11标准中,智能指针作为一种强大的资源管理工具被引入,极大地提升了代码的健壮性和安全性。其中,std::unique_ptr作为唯一所有权智能指针,以其独特的非拷贝特性及自动内存释放机制,成为现代C++编程中的重要组件。

std::unique_ptr是C++标准库提供的智能指针类型之一,它拥有对动态分配对象的唯一且不可共享的所有权。当std::unique_ptr实例销毁时,其所指向的动态分配内存会自动释放,有效防止了内存泄漏问题。std::unique_ptr是一个独享所有权的智能指针,它提供了一种严格语义上的所有权,包括:

1、拥有它所指向的对象。

2、无法进行复制构造,也无法进行复制赋值操作。也就是说,我们无法得到指向同一个对象的两个unique_ptr。但是,可以进行移动构造和移动赋值操作。

3、保存指向某个对象的指针,当它本身被删除释放的时候(比如:离开了某个作用域),会使用给定的删除器释放它指向的对象。

unique_ptr的核心特性

1、唯一所有权。

std::unique_ptr不允许复制或赋值给其他std::unique_ptr实例,仅可通过移动语义转移所有权。这意味着,任何时刻只有一个std::unique_ptr实例可以指向特定的动态分配内存。

2、自定义删除器。

std::unique_ptr允许指定自定义的删除器,在资源不再需要时,执行特定的清理操作。

3、异常安全。

std::unique_ptr构造和析构过程都是异常安全的,即使在构造过程中抛出异常,也不会造成内存泄漏。

4、空指针优化。

为了提升性能,std::unique_ptr通常采用空指针优化技术,即在不持有任何资源时,不会额外分配内存存储删除器。

unique_ptr的使用

使用unique_ptr,可以实现如下功能。

1、为动态申请的内存提供异常安全。

2、将动态申请内存的所有权传递给某个函数。

3、从某个函数返回动态申请内存的所有权。

4、在容器中保存指针。

下面是一段传统的会产生不安全异常的代码:

X* f()
{
    X* p = new X;
    // 做一些事情,中间可能会抛出某个异常
    return p;
}


解决方法是:使用unique_ptr来管理这个对象的所有权,由其进行这个对象的释放工作。

X* f()
{
    unique_ptr<X> p(new X);
    // 做一些事情,中间可能会抛出某个异常
    return p.release();
}


如果程序执行过程中抛出了异常,unique_ptr就会释放它所指向的对象。但是,除非我们真的需要返回一个内建的指针,我们还可以返回一个unique_ptr。

unique_ptr<X> f()
{
    unique_ptr<X> p(new X);
    // 做一些事情,中间可能会抛出某个异常
    return move(p);
}


现在,我们可以参考下面的示例代码这样使用函数f()。

{
    // 使用移动构造函数(move constructor)
    unique_ptr<X> q = f();
    // 使用q
    q->DoSomething();
    // 复制指针q所指向的对象
    X x = *q;
}
// 离开作用域,q以及它所指向的对象都会被删除释放


unique_ptr具有移动语义,所以我们可以使用函数f()返回的右值对q进行初始化,这样就简单地将所有权传递给了q。

注意事项

1、初始化与赋值。

可以通过构造函数直接初始化,并可以通过reset()成员函数改变所指向的对象,或者利用移动构造函数或移动赋值运算符转移所有权。

2、不支持浅拷贝。

因为std::unique_ptr强调唯一所有权,所以它不支持拷贝构造函数和拷贝赋值运算符,但支持移动构造和移动赋值。

3、数组形式。

std::unique_ptr还支持数组形式,用法为std::unique_ptr<T[]>,适用于动态分配的数组资源管理。

4、嵌套指针。

当需要管理指向动态分配对象的指针的指针时,可以使用std::unique_ptr<std::unique_ptr<T>>,但需谨慎处理生命周期和所有权转移。

总结

std::unique_ptr凭借其独特的唯一所有权特性,成为解决动态内存管理问题的重要工具,有助于编写简洁、高效且不易出错的C++代码。熟练掌握std::unique_ptr的应用场景与限制条件,对于理解现代C++编程范式和设计理念具有重要意义。


相关文章
|
2月前
|
编译器 程序员 定位技术
C++ 20新特性之Concepts
在C++ 20之前,我们在编写泛型代码时,模板参数的约束往往通过复杂的SFINAE(Substitution Failure Is Not An Error)策略或繁琐的Traits类来实现。这不仅难以阅读,也非常容易出错,导致很多程序员在提及泛型编程时,总是心有余悸、脊背发凉。 在没有引入Concepts之前,我们只能依靠经验和技巧来解读编译器给出的错误信息,很容易陷入“类型迷路”。这就好比在没有GPS导航的年代,我们依靠复杂的地图和模糊的方向指示去一个陌生的地点,很容易迷路。而Concepts的引入,就像是给C++的模板系统安装了一个GPS导航仪
123 59
|
2月前
|
存储 编译器 C++
【C++】面向对象编程的三大特性:深入解析多态机制(三)
【C++】面向对象编程的三大特性:深入解析多态机制
|
2月前
|
存储 编译器 C++
【C++】面向对象编程的三大特性:深入解析多态机制(二)
【C++】面向对象编程的三大特性:深入解析多态机制
|
2月前
|
编译器 C++
【C++】面向对象编程的三大特性:深入解析多态机制(一)
【C++】面向对象编程的三大特性:深入解析多态机制
|
2月前
|
存储 安全 编译器
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(一)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值
|
2月前
|
C++
C++ 20新特性之结构化绑定
在C++ 20出现之前,当我们需要访问一个结构体或类的多个成员时,通常使用.或->操作符。对于复杂的数据结构,这种访问方式往往会显得冗长,也难以理解。C++ 20中引入的结构化绑定允许我们直接从一个聚合类型(比如:tuple、struct、class等)中提取出多个成员,并为它们分别命名。这一特性大大简化了对复杂数据结构的访问方式,使代码更加清晰、易读。
41 0
|
3月前
|
编译器 C++ 计算机视觉
C++ 11新特性之完美转发
C++ 11新特性之完美转发
54 4
|
3月前
|
Java C# C++
C++ 11新特性之语法甜点1
C++ 11新特性之语法甜点1
34 4
|
3月前
|
安全 程序员 编译器
C++ 11新特性之auto和decltype
C++ 11新特性之auto和decltype
44 3
|
3月前
|
设计模式 缓存 安全
C++ 11新特性之week_ptr
C++ 11新特性之week_ptr
39 2