了解c++11新特性-智能指针

c++智能指针概念

1 智能指针的思想--C++RAII机制

        RAII是Resource Acquisition Is Initialization(wiki上面翻译成 “资源获取即初始化”)的简称,是C++语言的一种管理资源、避免泄漏的惯用法,利用的就是C++构造的对象最终会被析构销毁的机制。RAII的做法是使用一个对象,在其构造时获取对应的资源,在对象生命期内控制对资源的访问,使之始终保持有效,在对象生命期结束析构的时候,释放构造时获取的资源。

        我们知道,c++动态内存分配需要为所有的new 分配的内存手动delete掉对应普通指针指向的内存资源,否则可用内存可能会随着代码的调用越来越少,最终导致程序崩溃。

        我们也知道,当我们在一个函数内部使用局部变量,当退出了这个局部变量的作用域时,这个变量也就别销毁了;当这个变量是类对象时,这个时候,就会自动调用这个类的析构函数,而这一切都是自动发生的,不要程序员显式的去调用完成。

        那么这个也太好了,智能指针思想就是这样去完成的。智能指针就是利用RAII(Resource Acquisition Is Initialization)机制对普通指针进行的一层封装。这样使得智能指针的行为动作像一个指针,本质上却是一个对象,这样可以安全的管理一个对象的生命周期。

        智能指针 = 普通指针 + c++对象的构造/析构机制

2 智能指针种类

在c++中,智能指针一共定义了4种:
unique_ptr、shared_ptr、weak_ptr 和 auto_ptr。

其中,auto_ptr 在 C++11已被摒弃,在C++17中已经移除不可用,重点学习 前两种。这些类型都定义在头文件<memory>中。

std::weak_ptr: std::weak_ptr是一种弱引用智能指针,它用于解决std::shared_ptr可能导致的循环引用问题。std::weak_ptr可以观测到被std::shared_ptr管理的对象,但不拥有该对象,也不会增加引用计数。它可以通过std::shared_ptr进行构造或转换,用于临时访问被std::shared_ptr管理的对象,但不会延长对象的生命周期。

c++智能指针介绍

unique_ptr

        unique_ptr是一种独占智能指针,它提供了对动态分配的对象的独占所有权。它不允许多个unique_ptr 指向同一块内存,因此不能进行拷贝,只能进行移动(转移交出所有权给另一个智能指针)。当std::unique_ptr超出其作用域时,或者通过std::move将所有权转移给其他std::unique_ptr时,它指向的对象也会被自动摧毁,帮助程序员实现了自动释放的功能。

        unique_ptr 也可能还未指向对象,这时的状态被称为 empty。

        unique_ptr对象封装一个原始指针,并负责其生命周期。当该对象被销毁时,会在其析构函数中删除关联的原始指针。

创建unique_ptr:

可以先看一个简单的 unique_ptr 使用的例子:

动态创建一个一维数组 

unique_ptr<char> p(new char[10]);

        此时我们只要unique_ptr创建成功,unique_ptr对应的析构函数都能保证被调用,从而保证申请的动态资源能被释放掉。

对比-用普通指针用法:

char* p = new char[10];//开辟一个存放字符数组(包括10个元素)的空间,返回首元素的地址
delete[] p;

下面可以更详细的了解unique_ptr的构造和析构原型:

unique_ptr的构造方法:
// non-specialized 
template <class T, class D = default_delete<T>> class unique_ptr;
// array specialization   
template <class T, class D> class unique_ptr<T[],D>;

其中 T 指其管理的对象类型,D 指该对象销毁时所调用的释放方法,可以使用自定义的删除器,他也有一个默认的实现,即 detele 操作。

// unique_ptr constructor example
#include <iostream>
#include <memory>
 
int main () {
  std::default_delete<int> d;
  std::unique_ptr<int> u1;
  std::unique_ptr<int> u2 (nullptr);
  std::unique_ptr<int> u3 (new int);
  std::unique_ptr<int> u4 (new int, d);
  std::unique_ptr<int> u5 (new int, std::default_delete<int>());
  std::unique_ptr<int> u6 (std::move(u5));
  std::unique_ptr<int> u7 (std::move(u6));
  std::unique_ptr<int> u8 (std::auto_ptr<int>(new int));
 
  std::cout << "u1: " << (u1?"not null":"null") << '\n';
  std::cout << "u2: " << (u2?"not null":"null") << '\n';
  std::cout << "u3: " << (u3?"not null":"null") << '\n';
  std::cout << "u4: " << (u4?"not null":"null") << '\n';
  std::cout << "u5: " << (u5?"not null":"null") << '\n';
  std::cout << "u6: " << (u6?"not null":"null") << '\n';
  std::cout << "u7: " << (u7?"not null":"null") << '\n';
  std::cout << "u8: " << (u8?"not null":"null") << '\n';
 
  return 0;
}

//执行结果:
u1: null
u2: null
u3: not null
u4: not null
u5: null
u6: null
u7: not null
u8: not null

注意:不能像下面这样写。new得到的是一个普通指针,智能指针本质是一个对象,不能直接赋值,而是作为参数传递。

std::unique_ptr<int> p = new int(5)

但可以

shared_ptr<int> p = make_shared<int>(5);
自定义unique_ptr的析构方法 std::unique_ptr::~unique_ptr
// unique_ptr destructor example
#include <iostream>
#include <memory>
 
int main () {
  // user-defined deleter
  auto deleter = [](int*p){
    delete p;
    std::cout << "[deleter called]\n";
  };

  std::unique_ptr<int,decltype(deleter)> foo (new int,deleter);

  std::cout << "foo " << (foo?"is not":"is") << " empty\n";
  return 0; // [deleter called]
}

//输出
foo is not empty
[deleter called]
unique_ptr 不能被拷贝,但可以被移动-例子:

        因为不允许多个unique_ptr 指向同一块内存,因此不能进行拷贝,只能进行移动。所以unique_ptr也没有复制构造函数,不支持普通的拷贝和赋值操作。

std::unique_ptr<int>p1(new int(5));
std::unique_ptr<int>p2=p1;// 编译会出错
std::unique_ptr<int>p3=std::move(p1);// 转移所有权, 现在那块内存归p3所有, p1成为无效的针.
p3.reset();//释放内存.
p1.reset();//无效

shared_ptr

        我们提到的智能指针,很大程度上就是指的shared_ptr,因为它的性质基本跟我们使用的普通指针一致,可以多个指针指向同一块内存。

        它是一种引用计数智能指针,它允许多个指针共享同一块内存。每当创建一个std::shared_ptr指向同一个对象时,引用计数会增加。当释放一个指向该对象的std::shared_ptr时,引用计数会减少 1 个,当引用计数为零时,指向的内存会被自动释放。

        显然,share_ptr可以进行赋值拷贝。
 

创建shared_ptr
shared_ptr<int> p = make_shared<int>(1);
shared_ptr<int> p2(p);
shared_ptr<int> p3 = p;
**make_shared

        make_shared 是 c++11 加入标准库的,专门用于生成 std::shared_ptr  的模板函数。使用示例:

int  main()
{
    // 1.
    shared_ptr<string> p1(new string("66888"));
    cout << *p1 << endl;
 
    // 2.
    shared_ptr<string> p2 = make_shared<string>("888888");
    cout << *p2 << endl;
    
    return 0;
}

        我们更推荐使用 make_shared 创建 shared_ptr,性能和安全性更高。unique_ptr也同理,建议使用make_unique来创建。详细原因可参考:

C++ 学习系列 -- 智能指针 make_shared 与 make_unique_make_unique和make_shared-CSDN博客

通过智能指针的 get() 成员函数获得 其封装的普通指针: 
void func() {
    shared_ptr<int> p = make_shared<int>(1);
    int *p2 = p.get();
    cout<<*p2<<endl;
}

博文参考来源:

C++RAII机制:C++RAII机制-CSDN博客

智能指针:

c++智能指针详解_bitcarmanlee的博客-CSDN博客

智能指针 unique_ptr 详解-CSDN博客

cpp中易混淆总结(面经总结)-CSDN博客

C++ 学习系列 -- 智能指针 make_shared 与 make_unique_make_unique和make_shared-CSDN博客

  • 17
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值