普通指针
C、C++没有自动内存回收机制,程序员需要手动释放,如果忘记则会导致内存泄露,另外普通指针存在悬垂指针的问题。
何为悬垂指针?
当有多个指针指向同一个对象时,如果某个指针delete了该对象,对这个指针来说它是明确了它所指的对象被释放了,所以它不会再对此对象进行操作,但是对于剩下的其他指针来说呢?它们还傻傻地指向已经被删除的对象,并随时准备对它进行操作,于是悬垂指针就形成了。
/*************************************************************************
> File Name: 悬垂指针.cpp
> Author: Tanswer_
> Mail: 98duxm@gmail.com
> Created Time: 2016年10月10日 星期一 19时33分44秒
************************************************************************/
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <set>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <stack>
#include <queue>
#include <memory>
using namespace std;
int main()
{
//shared_ptr<string> 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 <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <set>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <stack>
#include <queue>
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 <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 &sp) :rp(sp.rp) { ++rp->count; }
//重载赋值操作符
SmartPtr& operator=(const SmartPtr<T>& 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<T> *rp; //辅助类对象指针
};
int main()
{
int *i = new int(2);
{
SmartPtr<int> ptr1(i);
{
SmartPtr<int> ptr2(ptr1);
{
SmartPtr<int> ptr3 = ptr1;
cout << *ptr1 << endl;
*ptr1 = 20;
cout << *ptr2 << endl;
}
}
}
return 0;
}
输出结果为:
2
20
还有2 个指针指向基础对象
还有1 个指针指向基础对象
OK~~~
此外,C++11新的标准库提供了两种智能指针类型来管理动态对象。