2.普通指针存在的问题
C措辞、C++措辞没有自动内存回收机制,关于内存的操作的安全性依赖于程序员的自觉。程序员每次new出来的内存块都须要自己利用delete进行开释,流程繁芜可能会导致忘却开释内存而造成内存泄露。而智能指针也致力于办理这种问题,使程序员专注于指针的利用而把内存管理交给智能指针。
我们先来看看普通指针的悬垂指针问题。当有多个指针指向同一个根本工具时,如果某个指针delete了该根本工具,对这个指针来说它是明确了它所指的工具被开释掉了,以是它不会再对所指工具进行操作,但是对付剩下的其他指针来说呢?它们还傻傻地指向已经被删除的根本工具并随时准备对它进行操作。于是悬垂指针就形成了,程序崩溃也“指日可待”。我们通过代码+图来来探求悬垂指针的办理方法。
int ptr1 = new int (1); int ptr2 = ptr1; int ptr3 = prt2; cout << ptr1 << endl; cout << ptr2 << endl; cout << ptr3 << endl; delete ptr1;互换群:875300321 cout << ptr2 << endl;
代码大略就不啰嗦阐明了。运行结果是输出ptr2时并不是期待的1,由于1已经被删除了。这个过程是这样的:

从图可以看出,缺点的产生来自于ptr1的”无知“:它并不知道还有其他指针共享着它指向的工具。如果有个办法让ptr1知道,除了它自己外还有两个指针指向根本工具,而它不应该删除根本工具,那么悬垂指针的问题就得以办理了。如下图:
那么何时才可以删除根本工具呢?当然是只有一个指针指向根本工具的时候,这时通过该指针就可以大大方方地把根本工具删除了。
3.什么是引用计数
如何来让指针知道还有其他指针的存在呢?这个时候我们该引入引用计数的观点了。引用计数是这样一个技巧,它许可有多个相同值的工具共享这个值的实现。引用计数的利用常有两个目的:
简化跟踪堆中(也即C++中new出来的)的工具的过程。一旦一个工具通过调用new被分配出来,记录谁拥有这个工具是很主要的,由于其所有者要卖力对它进行delete。但是工具所有者可以有多个,且所有权能够被通报,这就使得内存跟踪变得困难。引用计数可以跟踪工具所有权,并能够自动销毁工具。可以说引用计数是个大略的垃圾回收体系。这也是本文的谈论重点。节省内存,提高程序运行效率。如何很多工具有相同的值,为这多个相同的值存储多个副本是很摧残浪费蹂躏空间的,以是最好做法是让旁边工具都共享同一个值的实现。C++标准库中string类采纳一种称为”写时复制“的技能,使得只有当字符串被修正的时候才创建各自的拷贝,否则可能(标准库许可利用但没逼迫哀求)采取引用计数技能来管理共享工具的多个工具。这不是本文的谈论范围。4.智能指针实现理解了引用计数,我们可以利用它来写我们的智能指针类了。智能指针的实现策略有两种:赞助类与句柄类。这里先容赞助类的实现方法。
4.1.根本工具类首先,我们来定义一个根本工具类Point类,为了方便后面我们验证智能指针是否有效,我们为Point类创建如下接口:
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;互换群:875300321};
4.2.赞助类
在创建智能指针类之前,我们先创建一个赞助类。这个类的所有成员皆为私有类型,由于它不被普通用户所利用。为了只为智能指针利用,还须要把智能指针类声明为赞助类的友元。这个赞助类含有两个数据成员:计数count与根本工具指针。也即赞助类用以封装利用计数与根本工具指针。
class U_Ptr {private: friend class SmartPtr; U_Ptr(Point ptr) :p(ptr), count(1) { } ~U_Ptr() { delete p; } int count; Point p; };
4.3.为根本工具类实现智能指针类
引用计数是实现智能指针的一种通用方法。智能指针将一个计数器与类指向的工具干系联,引用计数跟踪共有多少个类工具共享同一指针。它的详细做法如下:
当创建类的新工具时,初始化指针,并将引用计数设置为1当工具作为另一个工具的副本时,复制布局函数复制副本指针,并增加与指针相应的引用计数(加1)利用赋值操作符对一个工具进行赋值时,处理繁芜一点:先使左操作数的指针的引用计数减1(为何减1:由于指针已经指向别的地方),如果减1后引用计数为0,则开释指针所指工具内存。然后增加右操作数所指工具的引用计数(为何增加:由于此时做操作数指向工具即右操作数指向工具)。析构函数:调用析构函数时,析构函数先使引用计数减1,如果减至0则delete工具。做好前面的准备后,我们可以来为根本工具类Point书写一个智能指针类了。根据引用计数实现关键点,我们可以写出我们的智能指针类如下:
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.4.智能指针类的利用与测试
至此,我们的智能指针类就完成了,我们可以来看看如何利用
int main(){ //定义一个根本工具类指针 Point pa = new Point(10, 20); //定义三个智能指针类工具,工具都指向根本类工具pa //利用花括号掌握三个指针指针的生命期,不雅观察计数的变革 { SmartPtr sptr1(pa);//此时计数count=1 { SmartPtr sptr2(sptr1); //调用复制布局函数,此时计数为count=2 { SmartPtr sptr3=sptr1; //调用赋值操作符,此时计数为conut=3 } 互换群:875300321 //此时count=2 } //此时count=1; } //此时count=0;pa工具被delete掉 cout << pa->getX ()<< endl; system("pause"); return 0;}
来看看运行结果咯:
还有2个指针指向根本工具还有1个指针指向根本工具-17891602请按任意键连续. . .
准期,在离开大括号后,共享根本工具的指针从3->2->1->0变换,末了计数为0时,pa工具被delete,此时利用getX()已经获取不到原来的值。
5.智能指针类的改进一
虽然我们的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; } 互换群:875300321 Point & operator () //重载操作符 { return (rp->p); } Point operator ->() //重载->操作符 { return rp->p; } private: U_Ptr rp; };
然后我们可以像指针般利用智能指针类
Point pa = new Point(10, 20); SmartPtr sptr1(pa); //像指针般利用 cout<<sptr1->getX();
目前这个智能指针智能用于管理Point类的根本工具,如果此时定义了个矩阵的根本工具类,那不是还得重新写一个属于矩阵类的智能指针类吗?但是矩阵类的智能指针类设计思想和Point类一样啊,就不能借用吗?答案当然是能,那便是利用模板技能。为了使我们的智能指针适用于更多的根本工具类,我们有必要把智能指针类通过模板来实现。这里贴上上面的智能指针类的模板版:
//模板类作为友元时要先有声明 template <typename T> class SmartPtr; template <typename T> class U_Ptr //赞助类 { private: //该类成员访问权限全部为private,由于不想让用户直策应用该类 friend class SmartPtr<T>; //定义智能指针类为友元,由于智能指针类须要直接操纵赞助类 //布局函数的参数为根本工具的指针 U_Ptr(T ptr) :p(ptr), count(1) { } //析构函数 ~U_Ptr() { delete p; } //引用计数 int count; //根本工具指针 T p; }; template <typename T> class SmartPtr //智能指针类 { public: SmartPtr(T ptr) :rp(new U_Ptr<T>(ptr)) { } //布局函数 SmartPtr(const SmartPtr<T> &sp) :rp(sp.rp) { ++rp->count; } //复制布局函数 SmartPtr& operator=(const SmartPtr<T>& rhs) { //重载赋值操作符 ++rhs.rp->count; //首先将右操作数引用计数加1, if (--rp->count == 0) //然后将引用计数减1,可以应对自赋值 delete rp; rp = rhs.rp; return this; } T & operator () //重载操作符 { return (rp->p); } T operator ->() //重载->操作符 { return rp->p; } ~SmartPtr() { //析构函数 if (--rp->count == 0) //当引用计数减为0时,删除赞助类工具指针,从而删除根本工具 delete rp; else cout << "还有" << rp->count << "个指针指向根本工具" << endl; } private: U_Ptr<T> rp; //赞助类工具指针 };
好啦,现在我们能够利用这个智能指针类工具来共享其他类型的根本工具啦,比如int:
int main(){ int i = new int(2); { SmartPtr<int> ptr1(i); { SmartPtr<int> ptr2(ptr1); { SmartPtr<int> ptr3 = ptr2; cout << ptr1 << endl; ptr1 = 20; cout << ptr2 << endl; } } } system("pause"); return 0;}
运行结果准期所愿,SmartPtr类管理起int类型来了:
2 20 还有2个指针指向根本工具 还有1个指针指向根本工具 请按任意键连续. . .
关注我 私信回答“资料” 获取更多 资料 条记 视频等