智能指针的概念和实现

概念

智能指针从字面上看,首先是一个对象,而它的行为(或接口)是在模拟一个指针,但又比指针更加智能。指针的主要作用是引用资源,指针的最大问题是在复杂情况下很难管理好它指向的资源的生命周期。因此智能指针要做的就是在管理资源生命周期这件事情上更加“智能”。C++11已经在<memory>模块中提供了常用的三种智能指针,它们分别是 unique_ptr, shared_ptrweak_ptr

但理解智能指针的原理和实现细节也是十分有意义的。了解原理可以保证更加合理的使用标准提供的智能指针,保证功能和效率;标准STL的指针就像其他STL工具一样,针对绝对大多数的生产场景保证功能和效率,但是在个别情况下可能不是最佳选择,过于臃肿拖沓,有时甚至无法使用(个别不支持C++11嵌入式开发,比如TI的TMS320系列编译器),如果你能够理解实现细节,就能实现裁剪版本或是修改版本的智能指针,保证效率和所需功能的最大化追求。

按照 Andrei Alexandrescu 在《现代C++设计》 一书中的思路,首先列出智能指针的策略点,就是可变化的功能块,再在下一部分描述实现时的注意事项。

  • 存储:智能指针首先要模拟通用指针,因此需要做到通用指针的最重要功能就,即存储资源:->,* 操作符和对原始资源的访问控制;
  • 所有权:所谓智能的部分就是所有权管理,或者叫做资源生命周期管理;
  • 值语义:由于不同于通用指针,它是一个对象,就具有了“值语义”,需要合理实现相等、比较、类型转换等操作;
  • 错误、异常管理;

实现

存储

智能指针内部需要有真正的资源“指针”,可以是 handle、文件 handle、原始指针,通常就是模板参数中那个最主要的类型 T,还需要实现 operator-> 和解引用的 operator*,以及是否向外界暴露真正的资源“指针”。操作符重载的方式都比较容易理解和实现,但对于暴露原始“指针”,C++ 标准将其作为成员函数shared_ptr::get,不见得是最好的方式。最好是能够用友元全局函数,毕竟指针指向对象的接口中难免也会有get函数,通过成员函数暴露原始资源指针,容易造成代码阅读的困扰。此外在销毁资源时,默认调用的是T的析构函数,也可以在template中提供接口来负责销毁资源。

所有权

所有权的变化体现在构造函数、析构函数、拷贝构造和赋值操作符的行为上,也是标准库提供的三种最重要智能指针的原因。在《现代C++设计》中提出的所有权策略更多,包括深拷贝、写时拷贝、引用计数、引用链表和破坏式拷贝。

  • unique_ptr : 不允许拷贝和赋值;只能够在构造函数中初始化,析构中销毁资源(破坏式拷贝);
  • shared_ptr : 允许拷贝和赋值,并在其中记录所有指向资源的智能指针;在资源初始化赋值给第一个智能指针时开始记录引用了该资源的智能指针,在最后一个指向该资源的智能指针的析构函数中销毁资源和用于记录结构(引用计数拷贝);
  • weak_ptr : 作为shared_ptr的观察者,用在shared_ptr指针系统中可能存在循环引用的地方,防止“你指向我,我指向你”导致的内存泄漏。因此它在的构造函数需要接收一个shared_ptr或者是weak_ptr (拷贝构造),而不能直接接受资源“指针”。而在拷贝、赋值时候需要能够持续监视shared_ptr内部的资源和记录结构,在析构时无论怎样都不销毁资源;

值语义

  • 首先不建议直接暴露原始资源,也就是不建议实现隐式类型转换为原始指针。如果非要这样,就需要禁止 delete 直接作用于智能指针,不然就不叫智能指针了(通过void*隐式转换,使得delete无法确认析构的确切调用方法);
  • 相等性操作符,等于和不等于,应该尽量保证和资源“指针”的含义一致。比如裸指针的相等判断的作用是判断是否是同一个资源;首先不能实现到bool的隐式类型转换,不然相等性不等性比较操作 (a == b; a != b)就会变得无效,因为操作符两边的智能指针先隐式转换为bool再进行等于与否的比较,显然不是希望的逻辑。由此带来的问题就是不能再出现这样的代码 if (p),顶多可以通过重载!操作符来实现这样的操作 if(!p)。同时为了能够比较基类和继承的子类,最好还要实现模板类式的比较操作。
  • 次序比较操作,原则上当你要比较两个指针的大小时,比较的是内存地址,因此也就意味着这是一个有先后、连续性开辟的内存空间,而这种内存空间往往不不需要进行资源的开辟和析构的智能管理。所以原则上不应该实现智能指针的加减、大小比较操作。如果非要实现,比较操作可以只实现 <>操作符,其他的都有它们来间接实现。因此可以先实现其它的操作符,留下<>的全局比较操作,由用户来决定是否实现次序比较。

错误异常管理

需要决定再构造函数中是否能够接收空指针,这也决定了在->操作符中是否要进行预检指针是否为空。在->操作符中进行检测,在频繁使用->操作符时候对性能的影响会逐渐明显;

参考实现

关于《现代 C++设计》

Andrei Alexandreu 在此书中将对象分解位一个个的策略点,我认为可以理解为变化点,将这些策略点抽象成为接口类,然后通过模板将这些抽象接口组装成为对象。通过实现自己的实体策略类,就可以组装出功能表现不同但语义相同的对象。这种方式灵活性非常大,性能也比多重继承高,但是对当时的编译器提出了太大的挑战,并没有成为太主流的设计方法。其实在设计上和代码的可读性上是非常有优势的,但是C++的模板奇淫技巧太多,给这种设计方式的学习和传播制造了太大的障碍。

原文地址:https://www.cnblogs.com/songyuncen/p/12076162.html

时间: 2024-10-10 23:34:45

智能指针的概念和实现的相关文章

C++11智能指针

C成也指针,败也指针.确实,指针给程序员提供了很多便利和灵活性,但是不当的指针使用也会造成很多问题. Java和C#避免了指针(虽然C#中也能使用指针,但是估计很少有人这样做),其垃圾回收机制,给程序员减轻很多管理内存的负担. 为了带来指针更好的使用体验,C++中引入了智能指针的概念,其实质就是将指针的一些操作封装成类,程序员通过使用熟悉的指针运算符(-> 和 *)访问封装指针,该指针类通过运算符重载返回封装的原始指针. C++ 智能指针思路类似于C#等语言中创建对象的过程:创建对象后让系统负责

C++11中智能指针的原理、使用、实现

目录 理解智能指针的原理 智能指针的使用 智能指针的设计和实现 1.智能指针的作用 C++程序设计中使用堆内存是非常频繁的操作,堆内存的申请和释放都由程序员自己管理.程序员自己管理堆内存可以提高了程序的效率,但是整体来说堆内存的管理是麻烦的,C++11中引入了智能指针的概念,方便管理堆内存.使用普通指针,容易造成堆内存泄露(忘记释放),二次释放,程序发生异常时内存泄露等问题等,使用智能指针能更好的管理堆内存. 理解智能指针需要从下面三个层次: 从较浅的层面看,智能指针是利用了一种叫做RAII(资

对智能指针的一些理解

c++本身不提供自动内存回收机制,所以每次我们有new一个对象或者数组,我们都是时时刻刻提醒自己要记得去delete new出来的对象,那样岂不是非常烦人?而且就算你记得在析构函数中delete new出来的对象,也同样会造成很多问题,比如一个class里面有个指针,初始化的时候new了一块内存,然后class 有个对象a,再通过class b=a复制一份a,这样其实两个对象分别有一个指针指向new的那块内存,当有一个对象生命周期结束的,析构函数释放了那块内存,然后另外一个对象生命周期结束再去释

C++ 智能指针auto_ptr详解

1. auto_ptr 的设计动机: 函数操作经常依照下列模式进行: 获取一些资源 执行一些动作 释放所获取的资源 那么面对这些资源的释放问题就会出现下面的两种情况: 一开始获得的资源被绑定于局部对象,那么当函数退出的时候,这些局部对象的析构函数被自动的调用,从而自动释放掉这些资源; 一开始获得的资源是通过某种显示手段获取,而且并没有绑定在任何对象身上,那么必须以显式的方式释放.这种情况常常发生在指针身上; 例子: 1 void f() 2 { 3 ClassA* ptr = new Class

智能指针 shared_ptr 解析

最近正在进行<Effective C++>的第二遍阅读,书里面多个条款涉及到了shared_ptr智能指针,介绍的太分散,学习起来麻烦,写篇blog整理一下. LinJM   @HQU shared_ptr是一个智能指针.在C++ 11颁布之前,它包含在TR1(Technical Report 1)当中,现在囊括在C++11的标准库中. 智能指针 智能指针(Smart pointers)是存储"指向动态分配(在堆上)的对象的指针"的对象.也就是说,智能指针其实是个对象.不过

Android系统篇之----Android中的智能指针

一.前言 今天我们开启Android系统篇的文章了,其实一直想弄,只是之前一直没有太多深入的了解,最近又把这块拿出来好好看了一下,所以想从新梳理一下,来看看Android中的这块知识,首先我们今天来看一下:Android中的智能指针的概念,为什么说先看一下智能指针这个知识呢?因为我们在看Android源码的时候,会发现几乎好多地方都用到了这个东东,所以我们在介绍后面的知识点,先来看看这个吧. 二.问题 那么Android中的智能指针是个什么东西呢?我们知道Android用的Java语言开发的,J

Android智能指针浅析

长久以来,C++中的内存管理问题一直让人头疼,空指针,野指针,内存泄露......C++程序员看到这样的问题就是各种头大!这样的问题往往很难解决,尤其是代码架构比较庞大或者复杂的时候.但是同样是面向对象的JAVA语言却没有这个问题,为什么呢?因为java有GC,也就是垃圾回收而C++没有.C++的做法是:程序员必须做到在new一个对象之后当不需要使用它的时候必须delete这个对象.看来很好,没有问题是吧?但是,在实际的项目中,就是会有人忘记去delete这个对象,或者随意delete一个对象,

ATL和vc++中的智能指针(分别是CComPtr和_com_ptr_t)

一.智能指针的概念 智能指针是一个类,不是指针,智能指针在所包含的指针不再被使用时候会自动释放该所包含指针所占用的系统资源,而不用手动释放. 原理:智能指针封装了包含指针的AddRef()函数和Release()函数,且在该类不被需要的时候在析构函数里调用包含指针的Release()函数释放包含指针的资源.因此实质是利用类的析构达到调用包含指针的Release()函数的目的. 二.VC++中的智能指针:_com_ptr_t _com_ptr_t实质是一个类模板.使用它时需要提供三个参数:接口的名

智能指针--转

1.智能指针的作用 C++程序设计设计中使用堆内存是非常频繁的操作,堆内存的申请和释放都由程序员自己管理.程序员自己管理内存可以提高程序的效率,但是整体来说堆内存的管理是麻烦的,C++11中引入了智能指针的概念,方便管理内存.使用普通指针,容易造成堆内存泄露(忘记释放),二次释放,程序发生异常时内存泄露等问题,使用智能指针能更好的管理堆内存. 智能指针从下面三个层次理解: 1.从较浅的层面,智能指针是利用叫做RALL(资源获取即初始化)的技术对普通的指针进行封装,这使得智能指针实质是一个对象,行