博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【C++】智能指针的设计与实现
阅读量:4184 次
发布时间:2019-05-26

本文共 4761 字,大约阅读时间需要 15 分钟。

普通指针

  C、C++没有自动内存回收机制,程序员需要手动释放,如果忘记则会导致内存泄露,另外普通指针存在悬垂指针的问题。

何为悬垂指针?

  当有多个指针指向同一个对象时,如果某个指针delete了该对象,对这个指针来说它是明确了它所指的对象被释放了,所以它不会再对此对象进行操作,但是对于剩下的其他指针来说呢?它们还傻傻地指向已经被删除的对象,并随时准备对它进行操作,于是悬垂指针就形成了。

/*************************************************************************    > File Name: 悬垂指针.cpp    > Author: Tanswer_    > Mail: 98duxm@gmail.com    > Created Time: 2016年10月10日 星期一 19时33分44秒 ************************************************************************/#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;int main(){ //shared_ptr
p1; int *ptr1 = new int(1); int *ptr2 = ptr1; int *ptr3 = ptr2; cout << *ptr1 << endl; cout << *ptr2 << endl; cout << *ptr3 << endl; delete ptr1; cout << *ptr2 << endl; return 0;}
代码很简单,我们可以看出错误产生来自ptr1并不知道还有其他指针共享着它所指向的对象,如果想办法让ptr1知道,除了自己外还有其他两个指针指向该对象,那么它不删除对象,问题就得到解决。

引用计数出现啦~~~

  智能指针的一种通用实现技术是使用引用计数器,智能指针类将一个计数器与类所指的对象相关联,引用计数器跟踪该类有多少个对象共享同一指针。

具体做法:

1).每次创建类的新对象时,初始化指针并将引用计数置为1;
2).当对象作为另一个对象的副本时,复制构造函数复制副本指针,并增加与指针引用相应的引用计数(加1);
3).使用赋值操作符对一个对象进行赋值时,处理复杂一点:先使左操作数的指针的引用计数减1(因为指针已经指向别的地方),如果减1后引用计数为0,则释放指针所指对象内存。然后增加右操作数所指对象的引用计数(因为此时左操作数指向对象即右操作数指向对象)。
4).析构函数:调用析构函数时,析构函数先使引用计数减1,如果减至0则delete对象。

设计智能指针

1.为了直观的表示,我们实例化一个基础对象--点

//基础对象类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;    int y;};

2.辅助类

/* * 辅助类,成员全部是私有,只为智能指针使用 * 用以封装使用计数与基础对象指针*/class U_ptr{private:    friend class SmartPtr;    U_ptr(Point *ptr): p(ptr),count(1) {}    ~U_ptr() { delete p; }    int count;  //计数    Point *p;   //基础对象指针};

3.为基础对象类实现智能指针类: 按以上介绍引用计数的做法,以引用计数为关键点

class SmartPtr{public:    SmartPtr(Point *ptr) :rp(new U_ptr(ptr)) {}    /*复制构造函数*/    SmartPtr(const SmartPtr &sp) :rp(sp.rp) { ++rp->count; }    SmartPtr& operator=(const SmartPtr& rhs)    {        ++rhs.rp->count;        if(--rp->count == 0)            delete rp;        rp = rhs.rp;        return *this;    }    ~SmartPtr()    {        if(--rp->count == 0)            delete rp;        else            cout << "还有" << rp->count << " 个指针指向基础对象" << endl;    }private:    U_ptr *rp;};

4.主函数

int main(){    //定义一个基础对象指针    Point *pa = new Point(10,20);    //定义三个智能指针类对象,都指向基础类对象pa    //使用花括号控制三个指针的生命期,观察计数的变化    {        SmartPtr sptr1(pa);  //此时count = 1        {            SmartPtr sptr2(sptr1); //调用复制构造函数 count  = 2            {                SmartPtr sptr3 = sptr1; //调用赋值操作符 count = 3            }//count = 2        } //count = 1    }//count = 0 pa 被delete    cout << pa-> getX() << endl;    return 0;}

输出结果:

还有2 个指针指向基础对象还有1 个指针指向基础对象0

  上述程序实现的智能指针不能像 正常指针那样有 * -> 等操作符,而且只是适应Point这个类对象,下面我们完成一个完整的智能指针,重载* -> 等操作符,用类模板来使我们的智能指针适用于更多的基础对象类。

/*************************************************************************    > File Name: 智能指针_3.cpp    > Author: Tanswer_    > Mail: 98duxm@gmail.com    > Created Time: 2016年10月10日 星期一 20时53分45秒 ************************************************************************///前两个例子只能用于管理Point类的基础对象//本例中我们设计模板 使我们的智能指针适用于更多的基础对象类#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;//基础对象类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; int y;};/* * 辅助类,成员全部是私有,只为智能指针使用 * 用以封装使用计数与基础对象指针*///模板类作为友元时要先有声明template
class SmartPtr;template
class U_ptr{private: //该类成员访问权限全部为private,因为不想让用户直接使用该类 friend class SmartPtr
; //因为智能指针需要直接操纵辅助类 //构造函数的参数为基础对象的指针 U_ptr(T *ptr): p(ptr),count(1) {} ~U_ptr() { delete p; } //引用计数 int count; //基础对象指针 T *p;};template
class SmartPtr //智能指针类{public: SmartPtr(T *ptr) :rp(new U_ptr
(ptr)) {} //构造函数 /*复制构造函数*/ SmartPtr(const SmartPtr &sp) :rp(sp.rp) { ++rp->count; } //重载赋值操作符 SmartPtr& operator=(const SmartPtr
& rhs) { ++rhs.rp->count; //首先将右操作数计数加1 if(--rp->count == 0) delete rp; rp = rhs.rp; return *this; } ~SmartPtr() { if(--rp->count == 0) //当引用计数减为0,释放指针,删除对象 delete rp; else cout << "还有" << rp->count << " 个指针指向基础对象" << endl; } T & operator *() //重载*操作符 { return *(rp->p); } T * operator ->() //重载->操作符 { return rp->p; }private: U_ptr
*rp; //辅助类对象指针};int main(){ int *i = new int(2); { SmartPtr
ptr1(i); { SmartPtr
ptr2(ptr1); { SmartPtr
ptr3 = ptr1; cout << *ptr1 << endl; *ptr1 = 20; cout << *ptr2 << endl; } } } return 0;}

输出结果为:

220还有2 个指针指向基础对象还有1 个指针指向基础对象

OK~~~

此外,C++11新的标准库提供了两种智能指针类型来管理动态对象。

转载地址:http://yluoi.baihongyu.com/

你可能感兴趣的文章
设置IP别名Shell脚本
查看>>
Source Insight 宏-单行注释
查看>>
levelDB源码分析-Arena
查看>>
levelDB源码分析-SSTable
查看>>
平滑升级Nginx的Shell脚本
查看>>
SSH远程会话管理工具
查看>>
canvas标签设长宽是在css中还是在标签中
查看>>
如何创建一个vue项目
查看>>
webpack和webpack-simple中如何引入css文件
查看>>
vue1.0和vue2.0的区别之路由
查看>>
关于vue-router2.0的学习笔记
查看>>
vue1.0与2.0区别之生命周期
查看>>
vue2.0之非父子组件通信
查看>>
如何建立svn版本库并运行它
查看>>
如何合并svn分支到主干上
查看>>
libusb源码学习:list_entry
查看>>
libusb源码学习:几个函数加载的宏(windows)
查看>>
MCU_如何通过硬件VID 查找生产厂家
查看>>
NCNN部署例程 mxnet-gluoncv之simple_pose
查看>>
Ubuntu18.04查看显卡信息并安装NVDIA显卡驱动driver + Cuda + Cudnn
查看>>