智能指针剖析

  1. auto_ptr

已经废弃。原因是它行为上是"排它性"指针,但又允许编译器实现拷贝操作,拷贝后的右值会被赋空。即将“传递”语义掩盖在“拷贝”动作之下。

即a=b时,作为右值的b的物理指针会是NULL。

会造成使用它的容器混乱。

这是典型的设计缺陷。既然是“传递”语义,就不应以“拷贝"形式出现。

另一方面,它对于数组的指针也支持不好,无法完成new []和delete []的配对。

  1. unique_ptr

这是对auto_ptr的“传递”语义的正确实现。从语言层面来保证不出现“拷贝”动作。

 1 typedef std::unique_ptr<int> unique_t;
 2 typedef std::vector< unique_t > vector_t;
 3
 4 vector_t vec1;                           // fine
 5 vector_t vec2(5, unique_t(new Foo));     // Error (Copy)
 6 vector_t vec3(vec1.begin(), vec1.end()); // Error (Copy)
 7 vector_t vec3(make_move_iterator(vec1.begin()), make_move_iterator(vec1.end()));
 8     // Courtesy of sehe
 9
10 std::sort(vec1.begin(), vec1.end()); // fine, because using Move Assignment Operator
11
12 std::copy(vec1.begin(), vec1.end(), std::back_inserter(vec2)); // Error (copy)
  1. shared_ptr

不像之前的unique_ptr,shared_ptr重点在于shared,即为了多个指针指向同一对象而生。同时,又在原生指针基础之上,加了引用计数。在如a=b的场景下,会自动增减引用的计数。如a如果原来指向一个对象,则由于此赋值动作,原先被指向的对象计数要减1,同时,b指向的对象计数要加1。

引用计数通过如下图的control block表示,总的的内存分配:

可见:

- shared_ptr占用空间是正常指针的两倍;

- control block记录了引用计数,同时还有其他的一些自定义的deleter等。

control block本身也是动态分配的,它在如make_shared,或由unique_ptr、raw ptr初始化时创建。

为此,要避免对于raw指针,多次重复创建shared_ptr,导致同一个object多个control block的情况。这会引起重复析构!

分以下情况避免:

如果由于需要定制化的deleter而不得不使用new时,要将其写在一行语句中。

1 std::shared_ptr<Widget> spw1(new Widget, xxxDel);

这样。

对于有的需要this指针的场合,有专门的语言层面解决方法:enable_shared_from_this,但需要调用前本对象已经有control block,即已经有shared_ptr指向本身。所以一般用在工厂模式中。

衡量下control block的具体开销:

  1. 大小,如上所述指针的大小外,本身的大小不会太大,一般也就3个字长;
  2. 解引用开销:可见,解引用是无额外开销的;
  3. 引用计数:原子操作的开销,与具体机器有关,但往往也是一条指令完成;
  4. 它内部还存在虚函数的调用。

如果对开销敏感,同时又需要“execlusive"语义,unique_ptr才是选择。unique还支持对[]T的指针,虽然没啥用。unique_ptr它可以转成shared_ptr,但反过来不行。

  1. weak_ptr

weak_ptr纯粹是作为shared_ptr的补充而存在。它不能解引用,不能判空,生命周期开始之初就需要与shared_ptr共存(只有通过shared_ptr才能初始化)。

它的作用是,在不占用shared_ptr引用计数的同时,判断出一个对象是否销毁。即判断shared_ptr中的引用计数是否为0了。(dangle指针)。这种状态对于weak_ptr来说有个专有名词,叫做expired。

这种检查有两种形式:

a. 通过weak_ptr的lock(),“原子性”的判断是否expired,如果是,返回shared_ptr,否则返回空;

b. 直接用来初始化一个shared_ptr,如果expired,则异常。

应用场景:

  1. 是可用于像观察者模式这种,需要持有某对象的指针,但这个“持有”动作并不应影响对象的生命周期。(观察者是否销毁不应由持有指针的subject影响);另一方面,subject又需要知道observer是否存活,这种情况,需要shared_ptr加weak_ptr的配合;
  2. 有一种场景,A与B需要互相持有对方指针,如果是shared_ptr,则会出现java式“内存泄漏”。需要用weak_ptr破除这种环形引用。
  1. 初始化与control block

使用new与使用make_shared初始化的区别:

- 需要明确的是,control block也是需要动态申请的内存。 make_shared时,可能会对shared_ptr的对象的内存与control block的内存一起申请。会在内存对齐,申请速度上有优势;

- 另一方面,new与shared_ptr两者分开进行时,如果编译器在new与shared_ptr初始化过程中,优化插入了一些可能抛出异常的代码,则会有内存泄漏。

- 但,在需要定制化deleter时,也需要new,这种代码要小心,写成异常安全的;

- 在构造函数初始化时,有传入{}和()的区别,特别对于如vector这种object,如果使用make_shared,要注意默认传的是()。即:

auto p = std::make_shared<std::vector<int> >(10, 20);

这种代码,到底是初始化了一个vecotr包含10, 20两个数字,还是10个元素的vector每一个都是20?

- 最需要理解的一点:如果是make_shared出来的内存,由于control block与object内存是绑定一起申请的,那么也要一起释放。而control block中,引用计数的值不仅shared_ptr会看,weak_ptr也会看。这样即使对象没有引用,引用计数为0了,但如果有weak_ptr存在,这control block就不能释放(要给weak_ptr判断用)。control block中的weak_counter就是记录weak_ptr的引用计数的。这在极端情况下,可能会造成一些影响。

时间: 2025-01-01 08:38:08

智能指针剖析的相关文章

C++智能指针剖析(下)boost::shared_ptr&amp;其他

1. boost::shared_ptr 前面我已经讲解了两个比较简单的智能指针,它们都有各自的优缺点.由于 boost::scoped_ptr 独享所有权,当我们真真需要复制智能指针时,需求便满足不了了,如此我们再引入一个智能指针,专门用于处理复制,参数传递的情况,这便是如下的boost::shared_ptr. boost::shared_ptr 属于 boost 库,定义在 namespace boost 中,包含头文件#include<boost/smart_ptr.hpp> 便可以使

STL(五)之智能指针剖析

_Mutex_base template<_Lock_policy _Lp> class _Mutex_base { protected: enum { _S_need_barriers = 0 }; }; template<> class _Mutex_base<_S_mutex : public __gnu_cxx::__mutex { protected: enum { _S_need_barriers = 1 }; }; _Sp_counted_base templa

(转)剖析C++标准库智能指针(std::auto_ptr)

不可否认,资源泄露(resource leak)曾经是C++程序的一大噩梦.垃圾回收 机制(Garbage Collection)一时颇受注目.然而垃圾自动回收机制并不能 满足内存管理的即时性和可视性,往往使高傲的程序设计者感到不自在. 况且,C++实现没有引入这种机制.在探索中,C++程序员创造了锋利的 "Smart Pointer".一定程度上,解决了资源泄露问题. 也许,经常的,你会写这样的代码: //x拟为class: // class x{ // public: // int

C++智能指针简单剖析

导读 最近在补看<C++ Primer Plus>第六版,这的确是本好书,其中关于智能指针的章节解析的非常清晰,一解我以前的多处困惑.C++面试过程中,很多面试官都喜欢问智能指针相关的问题,比如你知道哪些智能指针?shared_ptr的设计原理是什么?如果让你自己设计一个智能指针,你如何完成?等等--.而且在看开源的C++项目时,也能随处看到智能指针的影子.这说明智能指针不仅是面试官爱问的题材,更是非常有实用价值. 下面是我在看智能指针时所做的笔记,希望能够解决你对智能指针的一些困扰. 目录

简单剖析智能指针的思想

谈到智能指针之前,必须要知道堆栈的相关思想,堆栈在程序开发过程中经常使用到的.比如应用程序的函数当中通过new一个对象,这个对象的实际存储地方是在堆上,而它的指针是保存在栈,在函数执行完毕之后,该对象的指针会被自动从栈中弹出,而其指向的对象会在不会被自动释放,需要通过delete函数来释放该指针所指向的堆栈内存.在开发过程中往往因为忘记delete而造成内存泄露的情况.由此就有智能指针的出现. 智能指针主要思想是:在栈中new一个智能指针对象,而这个智能指针对象指向的实际内容是放在堆上的,在函数

【转】C++智能指针简单剖析

原文链接:http://www.cnblogs.com/lanxuezaipiao/p/4132096.html 导读 最近在补看 <C++ Primer Plus>第六版,这的确是本好书,其中关于智能指针的章节解析的非常清晰,一解我以前的多处困惑.C++面试过程中,很多面试官都喜欢问智能指针相关的 问题,比如你知道哪些智能指针?shared_ptr的设计原理是什么?如果让你自己设计一个智能指针,你如何完成?等等…….而且在看开源的C++项目 时,也能随处看到智能指针的影子.这说明智能指针不仅

智能指针的简单剖析和实现

在C语言中我们用指针来进行内存管理,这也是C语言的强大之处.然而,也正是指针的存在使得C语言变得令人懊恼,内存泄漏.垂悬指针等等问题.强大的C++则采用智能指针(Smart_Ptr)来处理这个问题. 好了,什么是智能指针呢?智能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象.这样以防止内存泄漏. 智能指针都有哪些种类呢? 通过上述表格可以看出有如此多的智能指针,C11标准库已经引进unique_ptr/shared_ptr/weak_ptr供我们使用. 下面来简单谈谈这些指针的原

深度剖析智能指针

RALL:资源分配即初始化,定义一个类来封装资源的分配和释放,在构造函数中完成资源的分配和初始化,在析构函数中完成资源的清理. 首先来看这样一个例子: 此例子乍一看上去,new/delete匹配,并没有什么错.但就因为return的存在,使得Test()函数提前结束,并没有执行delete p.这就使得内存泄露. 内存泄露的危害:使得可用内存越来越少,下一次开辟可能不够,程序崩溃. 为解决此类问题,引入智能指针. 所谓的智能指针就是智能/自动化的管理指针所指向的动态资源的释放. 智能指针的行为类

C++ Primer 学习笔记_56_STL剖析(十一)(原boost库):详解智能指针(unique_ptr(原scoped_ptr) 、shared_ptr 、weak_ptr源码分析)

注意:现在boot库已经归入STL库,用法基本上还和boost类似 在C++11中,引入了智能指针.主要有:unique_ptr, shared_ptr, weak_ptr. 这3种指针组件就是采用了boost里的智能指针方案.很多有用过boost智能指针的朋友,很容易地就能发现它们之间的关间: std boost 功能说明 unique_ptr scoped_ptr 独占指针对象,并保证指针所指对象生命周期与其一致 shared_ptr shared_ptr 可共享指针对象,可以赋值给shar