1. 为什么需要智能指针?简单的说,智能指针是为了实现类似于Java中的垃圾回收机制。Java的垃圾回收机制使程序员从繁杂的内存管理任务中彻底的解脱出来,在申请使用一块内存区域之后,无需去关注应该何时何地释放内存,Java将会自动帮助回收。但是出于效率和其他原因(可能C++设计者不屑于这种傻瓜氏的编程方式),C++本身并没有这样的功能,其繁杂且易出错的内存管理也一直为广大程序员所诟病。

更进一步地说,智能指针的出现是为了满足管理类中指针成员的需要。包含指针成员的类需要特别注意复制控制和赋值操作,原因是复制指针时只复制指针中的地址,而不会复制指针指向的对象。当类的实例在析构的时候,可能会导致垂悬指针问题。

管理类中指针成员的方法一般有两种方式:一种是采用值型类,这种类是给指针成员提供值语义(value semantics),当复制该值型对象时,会得到一个不同的新副本。这种方式典型的应用是string类。另外一种方式就是智能指针,实现这种方式的指针所指向的对象是共享的。

2. 智能指针的实现概述智能指针(smart pointer)的一种通用实现技术是使用引用计数(reference count)。智能指针类将一个计数器与类指向的对象相关联,引用计数跟踪该类有多少个对象共享同一指针。

每次创建类的新对象时,初始化指针并将引用计数置为1;当对象作为另一对象的副本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计数;对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数为减至0,则删除对象),并增加右操作数所指对象的引用计数;调用析构函数时,构造函数减少引用计数(如果引用计数减至0,则删除基础对象)。

实现智能指针有两种经典策略:一是引入辅助类,二是使用句柄类。

3. 实现方式1:引入辅助类这种方式定义一个单独的具体类(RefPtr)来封装指针和相应的引用计数。

class Point                                       //基础对象类

{

public:

Point(int xVal = 0, int yVal = 0):x(xVal),y(yVal) { }

int getX() const { return x; }

int getY() const { return y; }

void setX(int xVal) { x = xVal; }

void setY(int yVal) { y = yVal; }

private:

int x,y;

};

class RefPtr                                  //辅助类

{    //该类成员访问权限全部为private,因为不想让用户直接使用该类

friend class SmartPtr;                                  //定义智能指针类为友元,因为智能指针类需要直接操纵辅助类

RefPtr(Point *ptr):p(ptr), count(1) { }

~RefPtr() { delete p; }

int count;                                                     //引用计数

Point *p;                                                      //基础对象指针

};

class SmartPtr                                             //智能指针类

{

public:

SmartPtr(Point *ptr):rp(new RefPtr(ptr)) { }                                 //构造函数

SmartPtr(const SmartPtr &sp):rp(sp.rp) { ++rp->count; }            //复制构造函数

SmartPtr& operator=(const SmartPtr& rhs) {                              //重载赋值操作符

++rhs.rp->count;                                                                        //首先将右操作数引用计数加1,

if(--rp->count == 0)                                                                     //然后将引用计数减1,可以应对自赋值

delete rp;

rp = rhs.rp;

return *this;

}

~SmartPtr() {                                            //析构函数

if(--rp->count == 0)                                  //当引用计数减为0时,删除辅助类对象指针,从而删除基础对象

delete rp;

}

private:

RefPtr *rp;                                                //辅助类对象指针

};

int main()

{

Point *p1 = new Point(10, 8);

SmartPtr sp1(p1);

SmartPtr sp2(sp1);

Point *p2 = new Point(5, 5);

SmartPtr sp3(p2);

sp3 = sp1;

return 0;

}

使用该方式的内存结构图如下:

4. 实现方式2:使用句柄类为了避免上面方案中每个使用指针的类自己去控制引用计数,可以用一个类把指针封装起来。封装好后,这个类对象可以出现在用户类使用指针的任何地方,表现为一个指针的行为。我们可以像指针一样使用它,而不用担心普通成员指针所带来的问题,我们把这样的类叫句柄类。在封装句柄类时,需要申请一个动态分配的引用计数空间,指针与引用计数分开存储。实现示例如下:

class Point                                                  //基础对象类

{

public:

Point(int xVal = 0, int yVal = 0):x(xVal),y(yVal) { }

int getX() const { return x; }

int getY() const { return y; }

void setX(int xVal) { x = xVal; }

void setY(int yVal) { y = yVal; }

public:

virtual Point* clone() const {               //虚函数,为了实现让句柄类在不知道对象的确切类型的情况下分配已知对象的新副本

return new Point(*this);

}

private:

int x,y;

};

class D3Point : public Point                           //派生类

{

public:

D3Point(int xVal, int yVal, int zVal):Point(xVal, yVal), z(zVal) { }

int getZ() const { return z; }

void setZ(int zVal) { z = zVal; }

public:

D3Point* clone() const {                 //虚函数,为了实现让句柄类在不知道对象的确切类型的情况下分配已知对象的新副本

return new D3Point(*this);

}

private:

int z;

};

class SmartPtr

{

public:

SmartPtr(Point *ptr = 0):p(ptr), count(new int(1)) { }                                         //构造函数

SmartPtr(Point &point):p(point.clone()), count(new int(1)) { }                          //构造函数

SmartPtr(const SmartPtr &sp):p(sp.p), count(sp.count) { ++*count; }             //复制构造函数

SmartPtr& operator=(const SmartPtr &sp) {                                                   //重载赋值操作符

++*sp.count;                                           //首先将右操作数引用计数加1,

decr_use();                                             //然后将引用计数减1,可以应对自赋值

p = sp.p;

count = sp.count;

return *this;

}

~SmartPtr() {                                          //析构函数

decr_use();

}

public:                                   //一般情况下不会实现这两个操作符,因为我们不希望用户直接操纵基础对象指针

const Point* operator->() const {

if(p) return p;

else throw logic_error("Unbound Point");

}

const Point& operator*() const {

if(p) return *p;

else throw logic_error("Unbound Point");

}

private:

void decr_use() {

if(--*count == 0)

{

delete p;

delete count;

}

}

private:

Point *p;                                      //基础对象指针

int *count;                                   //指向引用计数的指针

};

int main()

{

Point *p1 = new Point(10, 8);

SmartPtr sp1(p1);

SmartPtr sp2(sp1);

D3Point *p2 = new D3Point(5, 5, 0);

SmartPtr sp3(p2);

return 0;

}

使用该方式的内存结构图如下:

c语言智能指针是什么,C++ 智能指针深入解析相关推荐

  1. 【C++ 语言】智能指针 引入 ( 内存泄漏 | 智能指针简介 | 简单示例 )

    文章目录 I . 智能指针 引入 II . 智能指针 简介 III . 智能指针 简单示例 I . 智能指针 引入 1 . 示例前提 : 定义一个 Student 类 , 之后将该类对象作为智能指针指 ...

  2. C++智能指针使用指南 part2:智能指针本身的方法以及使用建议

    目录 往期文章 智能指针本身的方法 对于unique_ptr 对于shared_ptr 对于weak_ptr 使用建议 1.使用工厂函数而非new构造对象 2.在类内部调用其他类的方法 3.在某类内部 ...

  3. C++智能指针(一)智能指针的简单介绍

    https://blog.csdn.net/nou_camp/article/details/70176949 C++智能指针  在正式了解智能指针前先看一下下面的一段代码 #include<i ...

  4. Android 智能指针 视频,Android系统智能指针中轻量级指针

    lp.sp.wp在Android Native层中被大量使用,所以非常有必要学习它们的实现原理.lp是Light Pointer的缩写,表示轻量级指针,sp是Strong Pointer的缩写,表示强 ...

  5. 智能指针(一)—— 智能指针的底层原理(RAII特性)

    我们使用 new关键字 或者 malloc函数 开辟一块空间时,因为这块空间是在堆上开辟的,如果不手动释放,即便出了作用域,这块空间也依然存在,这个时候就会造成内存泄漏. 为了保证资源的释放,我们可以 ...

  6. java寻优算法_模拟退火算法SA原理及python、java、php、c++语言代码实现TSP旅行商问题,智能优化算法,随机寻优算法,全局最短路径...

    模拟退火算法SA原理及python.java.php.c++语言代码实现TSP旅行商问题,智能优化算法,随机寻优算法,全局最短路径 模拟退火算法(Simulated Annealing,SA)最早的思 ...

  7. opencv 表识别 工业表智能识别 数字式表盘识别,指针式表盘刻度识别,分为表检测,表盘纠正,刻度分割,刻度拉直识别

    opencv 表识别 工业表智能识别 数字式表盘识别,指针式表盘刻度识别,分为表检测,表盘纠正,刻度分割,刻度拉直识别 _:34400640060136589IT干将

  8. opencv 表识别 工业表智能识别 数字式表盘识别,指针式表盘刻度识别,分为表检测

    opencv 表识别 工业表智能识别 数字式表盘识别,指针式表盘刻度识别,分为表检测,表盘纠正,刻度分割,刻度拉直识别 YYID:37400640060136589

  9. 区块链开发语言python_Python:不同区块链智能合约开发语言的选择

    链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载. 在本文中,将介绍比特币.超级账本Fabric和以太坊这三种区块链中,分别使用什么开发语言来进行智能合约的编程,并提供你进 ...

最新文章

  1. openssl生成https证书
  2. 不平等博弈问题学习记录(二)(对于超实数在博弈下左右相等的扩充)
  3. Linux NULL定义
  4. python xgboost用法_XGBoost使用教程(纯xgboost方法)一
  5. IDEA常用插件整理
  6. React 开发常见报错解决方法
  7. 三维空间点到直线的距离C++实现
  8. MAC删除自带ABC输入法
  9. 温习linux的常用命令
  10. C语言实现简单的电子通讯录
  11. Function.identity()初探
  12. 汇编程序设计:钟表显示
  13. 第三届长安杯解析(2次修订版)镜像+具体解析+个人详细解题过程,涉及多个模块,我会努力把所有写好,可以做一下题目,提升很明显。
  14. ps之解决eps图片不能保存为png格式问题
  15. 51单片机(入门保姆级教程)——LED闪烁及流水灯
  16. 20多份软件测试报告模板(标准版)一份优秀测试报告模板流程
  17. TheProjetXXXXXneedstobedeployedbeforeitanbestarted
  18. 数据库Mysql基础------第一部分 数据的准备与基础命令
  19. 苹果手机用什么软件测试续航,iOS 14.6负优化:测试发现7款iPhone机型的续航均下滑...
  20. 京东面试测试开发工程师

热门文章

  1. H5 --力导向图、关系图谱
  2. Android Theme主题
  3. Wi-Fi Direct
  4. 程序员学习视频教程汇总
  5. 夺命雷公狗---Redis---3-Redis常用命令
  6. php输出语句echo、print、print_r、printf、sprintf、var_dump比较
  7. HDU ACM 3986 Harry Potter and the Final Battle(邻接表实现最短路dijkstra堆优化记录路径 + 枚举最短路上每条边)...
  8. ELK下es的分词器analyzer
  9. Linux中,Mysql安装
  10. 如何解决从VBA中复制出的代码是乱码这一问题