C++11智能指针

您所在的位置:网站首页 release和released的区别 C++11智能指针

C++11智能指针

2024-07-09 09:02| 来源: 网络整理| 查看: 265

目录

一、动态内存之内存泄漏与野指针

二、shared_ptr类

make_shared函数

shared_ptr的拷贝与赋值

shared_ptr与普通指针

智能指针陷阱

三、unique_ptr类 

四、weak_ptr类

一、动态内存之内存泄漏与野指针

C++中一般用new和delete这一对运算符来进行动态内存管理。动态内存使用时很容易出现两中问题,即内存泄漏与野指针。此外关于new还有一些很有趣的点,new也可以分配const常量对象,其返回一个常量指针。

当自由空间耗尽的时候,new分配空间将会失败,默认情况抛出一个类型为bad_alloc的异常,但是可以用定位new(定位new表达式允许我们向new传递额外的参数)来改变这种行为,使得分配失败的时候返回一个空指针。另外还需要注意delete掉一个指针之后要将重置,比如重置为nullptr,则将产生空悬指针,相当于野指针。

        内存泄漏:用new为对象分配空间以后没有及时用delete销毁对象并释放空间,导致在程序运行过程中这一段内存始终被占用。比如对于一个动态分配的内置类型对象i,如果i是局部变量则当i离开其作用域后i会被销毁,但是其占用的内存不会被释放。

        野指针:比如多个指针指向同一块内存,当尚有指针引用这一块内存的情况下,我们就通过其它指针释放了这一块内存,那么此时就产生了引用非法内存的野指针。

        C++11中为了更安全地使用动态内存,新的标准库提供了智能指针类来管理动态对象。shared_ptr允许多个指针指向同一个对象,unique_ptr独占所指向地对象,weak_ptr是一种伴随类,指向shared_ptr所管理的对象。智能指针类也是模板,当定义智能指针对象的时候必须先提供该指针可以指向的类型。智能指针类型都定义在memory头文件中。

        下表中给出了shared_ptr和unique_ptr都支持的操作以及shared_ptr的一些特有的操作。

二、shared_ptr类

如下图中定义了两个智能智能对象,分别可以指向不同的类型,图中两个智能指针对象都是默认初始化的,其保存着一个空指针,意思是当前指针没有指向任何对象,类似nullptr初始化的指针。智能指针在使用的时候与普通指针是类似的,比如解引用以及作为条件语句的条件使用都是可以的。

 

make_shared函数

make_shared函数用来在动态内存中构造一个对象并初始化该对象,返回指向该对象的shared_ptr智能指针。如果我们没有提供初始值,那么将执行值初始化。一般我们可以用auto来接收make_shared函数的返回结果。make_shared与emplace类似,直接利用给定的参数来构造给定类型的对象,而不是把给定的参数用完即销毁。

shared_ptr的拷贝与赋值

当进行拷贝或者赋值操作时,每个shared_ptr指针都会记录有多少个其它的shared_ptr指针与其指向相同的对象。可以认为每个shared_ptr都有一个关联的计数器,称为引用计数。无论何时我们拷贝一个shared_ptr,计数器都会递增。比如我们用一个shared_ptr初始化了另一个shared_ptr,或者以值传递的方式传递给了一个函数,或者作为函数的返回值返回等。只要发生了拷贝,引用计数都会加1。当一个shared_ptr被销毁(比如一个局部shared_ptr离开其作用域),引用计数 就会自动减1。一旦一个shared_ptr的计数器变为0,它就会自动释放自己所管理的对象。

shared_ptr类也有自己的析构函数,当一个share_ptr被销毁时,析构函数就会递减它所指向对象的引用计数,如果引用计数变为0,shared_ptr就会销毁对象,并释放内存。对于一个普通指针来说,离开其作用域时指针会被销毁,但是其关联的动态内存不会被自动释放。

shared_ptr与普通指针

归根结底一句话,普通指针(当然必须是指向动态内存的普通指针)不能隐式转换为shared_ptr智能指针,因此要用直接初始化形式把一个普通指针转化成智能指针。

 

 

 

 

一些定义和改变shared_ptr的方法如下:

 

 如上图,当q是堆上的T类型对象的指针时,shared_ptr p(q)相当于用智能指针p来接管了普通指针q指向的对象。当q不是T类型对象的指针时,如果q能xiaogu转换成T类型对象的指针,则p也能接管q指向的对象,只不过得重新定义一个可调用对象d来代替delete,用于释放其关联的内存。

        虽然这一小节中讲了怎么把内置指针管理的动态内存交给智能指针管理,但实际使用中,应当避免同时使用普通指针和智能指针,因为shared_ptr可以用引用计数来管理内存的前提是所有引用同一块内存的智能指针对象都是相互的拷贝。而如果我们把一个普通指针指向的内存同时绑定到了多个独立创建的智能指针对象上,则将不能实现智能指针的功能,因为独立创建的不同的智能指针对象,即便引用同一块内存,其引用计数之间也是相互独立的。因此,应该尽量用make_shared来直接构造智能指针对象,而不是先定义普通指针,再用普通指针来初始化智能指针。

智能指针陷阱

三、unique_ptr类 

unique_ptr独占它所管理的对象,当unique_ptr指针销毁时,其所管理的内存空间也被释放。下表中时unique_ptr独有的操作。

因为unique_ptr没有类似make_shared的函数,因此一般都是以直接初始化的方式将其绑定到一个new返回的指针上。unique_ptr独占对象意味着不支持拷贝或赋值操作。

注意release方法返回一个unique_ptr管理的内置指针,并将unique_ptr置空,一般用release的返回值来直接初始化另一个智能指针,因为release只是切断了智能指针与对象之间的联系,但是并没有释放该对象所占空间,因此我们要保证这块空间被另一个智能指针管理,否则我们就要手动管理内存了。release和reset的核心区别就一点,release放弃对原内存的管理,但不释放原内存,reset放弃原内存的管理转而去管理另一块内存,且同时释放原内存。

 

 unique_ptr智能指针一般不能拷贝,但也有一个例外。如果一个unique_ptr对象即将被销毁,那么我们可以拷贝这个对象,最常见的例子是unique_ptr对象作为返回值返回的时候。

如上边图中两个例子,不管是返回一个直接创建的智能指针,还是返回一个局部对象,都会发生智能指针的拷贝,但前提是我们已经知道拷贝的源对象马上就要被销毁。

四、weak_ptr类

是定义再shared_ptr的基础之上的一种智能指针,指向一个由shared_ptr管理的对象,但是将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。

因为weak_ptr不影响管理对象的生存期,所以weak_ptr管理的对象随时可能被释放掉,所以我们不能用weak_ptr来直接解引用,而应该用lock函数来返回一个与其管理同一个对象的shared_ptr。

 

weak_ptr是为了解决shared_ptr的固有缺陷,即循环引用问题,循环引用问题导致即使是用智能指针,依然有内存泄漏的风险。具体可见https://blog.csdn.net/sai_j/article/details/82908241

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3