[C++] 初识 智能指针

水墨不写bug 2024-10-04 10:05:02 阅读 64

标题:[C++] 初识 智能指针

@水墨不写bug



目录

一、前言

二、智能指针

1. 什么是RAII?

2.智能指针分类

 三、智能指针简介

1.std::auto_ptr

2.std::unique_ptr

3.std::shared_ptr


正文开始:

一、前言

        C++智能指针的出现是有一定的背景的:

        Java有专属的GC(垃圾回收功能),这样一来可以在一定程度上解放解放程序员的关于内存方面的思考与手动设计,但GC等特点也是Java相对于C++效率较低的原因。在使用C++编程时,需要时刻注意内存的管理,但是尽管我们时刻都记得要注意内存的管理逻辑,在有一些场景,内存泄漏时有发生,比如下边这样的一个场景:

<code>#include<iostream>

#include<string>

using namespace std;

double div(double a, double b)

{

if (b == 0)

throw string("div by zero");

return a / b;

}

void func()

{

int* parr1 = new int[10]; // 一

int* parr2 = new int[10]; // 二

//...

double a, b;

cin >> a >> b;

cout << div(a,b) << endl; // 三

delete[] parr1;

delete[] parr2;

}

int main()

{

try {

func();

}

catch (string s)

{

cout << s << endl;

}

catch (...)

{

cout << "unknown err" << endl;

}

return 0;

}

        在上述场景中,有三个地方可能会抛出异常:如果一处抛异常——则不用释放空间;如果二处抛异常——则需要只释放parr1;如果三处抛异常——则需要释放parr1和parr2。

        这些逻辑比较复杂,为了处理这些场景下复杂的内存管理,C++引入了智能指针

二、智能指针

1. 什么是RAII?

           C++中的RAII(Resource Acquisition Is Initialization,资源获取即初始化)是一种资源管理技术,它的核心思想是将资源的获取(Acquisition)与对象的初始化(Initialization)结合起来,并通过对象的生命周期来管理资源的使用期。一旦对象被创建,资源就被自动获取;当对象生命周期结束时(例如,对象被销毁时),其析构函数会自动释放或归还资源

借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:

        1.不需要显式地释放资源。

        2.采用这种方式,对象所需的资源在其生命期内始终保持有效

 

         智能指针的实现思路及原理就是RAII的思想。


2.智能指针分类

         目前常用的智能指针有如下几种:

        std::auto_ptr:C++98版本的库中就提供了auto_ptr的智能指针。(不建议使用)

        std::unique_ptr:unique_ptr的实现原理:简单粗暴的防拷贝。

        std::shared_ptr:C++11中开始提供更靠谱的并且支持拷贝的shared_ptr。

 三、智能指针简介

        其实智能指针的基本原理的实现并不困难,就是把一个指针类中,写好构造和析构就可以正常使用了——当前函数栈桢退出,局部变量就会自动销毁。不同的是不同智能指针对于拷贝构造和赋值重载的处理是不同的,而其他接口的处理基本是相同的。

1.std::auto_ptr

        auto_ptr的实现原理:管理权转移。

        std::auto_ptr是比较老的智能指针类型,它可以拷贝构造,但是拷贝构造不是意味着构造了一个新的指针,而是将管理权转移到了新的指针;赋值重载会把原资源释放,把新资源转移到被赋值的指针中。

#include<memory>

#include<iostream>

using namespace std;

int main()

{

auto_ptr<int> ap(new int[10]);

auto_ptr<int> ap1(ap);

if (ap.get() == nullptr)

cout << "权限转移:ap->ap1" << endl;

auto_ptr<int> ap2;

ap2 = ap1;

if(ap1.get() == nullptr)

cout << "权限转移:ap1->ap2" << endl;

return 0;

}

权限转移:ap->ap1

权限转移:ap1->ap2

 


2.std::unique_ptr

        unique_ptr的实现原理:简单粗暴的防拷贝。

        强行将赋值重载和拷贝构造设置为 无法调用:具体可能是 直接加上 = delete ;或者是只声明不实现,并且放在私有。

        其余部分接口的使用与auto_ptr 基本一致。

 


3.std::shared_ptr

        shared_ptr的原理:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。

引用计数原则: 

         1. shared_ptr在其内部,给每个资源都维护了着一份计数p_count,用来记录该份资源被几个指针维护。

        2. 在对象(指针)被销毁时(也就是析构函数调用时),就说明这个对象(指针)不维护该资源了,对象的引用计数减一。

        3. 如果引用计数减到0,就说明自己是最后一个使用该资源的对象,必须释放该资源;

        4. 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了。

         shared_ptr由于通过引用计数实现,其使用方法最接近普通指针,所以这里不再举例如何使用。我们仅仅需要对其几个常用接口熟悉即可。

 std::shared_ptr文档


对于以上的三种智能指针,其底层实现我们暂时不涉及,本篇只设计其思想与使用方法。 


完~

未经作者同意禁止转载 



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。