1、智能指针的设计思想
我们一般通过在栈上创建一块内存管理堆上开辟的内存,根据堆栈的特点,我们可以知道,栈上的内存一般是由系统负责回收的!而堆上的内存则需要我们自己进行释放,忘记释放就会早成内存泄漏,因此,智能指针的设计思想就是为了满足内存由程序员开辟,然后由系统自动释放。就是将基本类型的指针封装为类对象模板,并在析构函数里释放的时候删除指向堆上的内存空间。
2、智能指针之auto_ptr
这是一个所有权唯一的智能指针,即管理权唯一,释放权唯一,当俩个指针指向同一块内存的时候,auto_ptr处理方案是将一个指向,一个置空,因此其使用场景比较单一。
代码实例如下:
template<typename T>
class Auto_ptr
{
public:
Auto_ptr(T* ptr) :mptr(ptr){} //构造函数
~Auto_ptr() //析构函数
{
delete mptr;
mptr = NULL;
}
Auto_ptr(const Auto_ptr<T>& rhs) //拷贝构造
{
mptr = rhs.mptr;
rhs.Release(); //变更管理权
}
Auto_ptr<T>& operator=(const Auto_ptr<T>& rhs) //赋值运算符重载
{
if (this != &rhs) //自赋值判断
{
delete mptr; //释放旧资源
mptr = rhs.mptr; //开辟新资源
rhs.Release(); //变更管理权
}
return *this;
}
T& operator*() // *运算符重载
{
return *mptr;
}
T* operator->() // ->运算符重载
{
return mptr;
}
private:
void Release()const //变更管理权,新指针有效,旧指针置空
{
(T*)mptr = NULL;
}
T* mptr;
};
3、智能指针之带标志位的auto_ptr
特点是所有权不唯一,释放权唯一,但是释放权可以转让。
template<typename T>
class Auto_ptr
{
public:
Auto_ptr(T* ptr) :mptr(ptr), flag(true){} //构造函数
Auto_ptr(const Auto_ptr<T>& rhs) //拷贝构造
{
mptr = rhs.mptr;
flag = rhs.flag;
rhs.flag = false;
}
Auto_ptr<T>& operator=(const Auto_ptr<T>& rhs) //赋值运算符重载
{
if (this != &rhs) //自赋值判断
{
this->~Auto_ptr(); //调用析构释放旧资源
mptr = rhs.mptr; //赋值新资源
flag = rhs.flag; //更新标志位
rhs.flag = false;
}
return *this;
}
~Auto_ptr() //析构函数
{
if (flag)
{
delete mptr;
}
mptr = NULL;
}
T& operator*() // * 运算符重载
{
return *mptr;
}
T* operator->() // ->指向运算符重载
{
return mptr;
}
private:
T* mptr;
mutable bool flag; //是否拥有释放权
}; //mutable:去除常性,被mutable修饰的变量,将永远处于可变的状态,即使是const函数中也可以改变这个变量的值。
但是其也有缺点:释放权的转移有时候会导致堆内存提前释放。比如改智能指针不能作为函数参数,因为在函数堆栈调用过程中,实参传形参是一个初始化的过程,函数结束,栈帧回退,参数中有释放权的指针就会提前调用delete,释放内存。代码示例如下:
#include<iostream>
using namespace std;
void functional(Auto_ptr_flag<int> ptr) //示例函数
{
cout << "functional" << endl;
}
int main()
{
Auto_ptr_flag<int> auto_flag1(new int); //开辟一块堆内存
Auto_ptr_flag<int> auto_flag2(auto_flag1); //调用拷贝构造生成新的指针指针
functional(auto_flag2); //调用函数
*auto_flag2 = 30; //函数已经退出,指针可能提前释放,存在bug
cout << "acomplish" <<endl;
return 0;
}
4、智能指针之score_ptr( 它能保证在离开作用域之后它所管理对象能被自动释放。)
scoped_ptr是boost库中智能指针,类似于c++11中unique_ptr,它是是boost库中的最简单的智能指针,它的特点是:一块堆内存,只允许一个引用,它能保证在离开作用域之后它所管理对象能被自动释放。scoped_ptr不能通过其他scoped_ptr共享控制权,因为其屏蔽了所有的拷贝构造函数和赋值运算符重载函数。为了更加严谨,除了将其私有化之外,还应该做到只申明,不实现的要求,因为友元可以访问类的私有成员。
代码实现如下:
#include<iostream>
template<typename T>
class Scope_Ptr
{
public:
Scope_Ptr(T* ptr) :mptr(ptr) //构造函数
{
std::cout<<"Scope_Ptr(T* ptr) :mptr(ptr)"<<std::endl;
}
~Scope_Ptr() //析构函数
{
std::cout<<"~Scope_Ptr()"<<std::endl;
delete mptr;
mptr = NULL;
}
T* operator->() // ->运算符重载函数
{
return mptr;
}
T& operator*() // * 运算符重载函数
{
return *mptr;
}
private:
Scope_Ptr(const Scope_Ptr<T>&); //拷贝构造函数
Scope_Ptr<T>& operator=(const Scope_Ptr<T>&); //赋值运算符重载函数
T* mptr;
};
int main()
{
int* p = new int;
Scope_Ptr<int> sp1(p);
Scope_Ptr<int> sp2(p);
Scope_Ptr<int> sp3(p); //返回的都是一个地址
return 0;
}
6、智能指针之share_ptr
share_ptr的特点是:带引用计数的智能指针,当有一个智能指针指向内存时,计数器加一;delete的时候,只有引用计数为0时,才可以释放。
class Ref_Management //引用计数管理
{
public:
static Ref_Management* getInstance() //提供公有接口,用于单例模式创建对象
{
return &rm;
}
void addref(void* mptr) //添加引用计数
{
if (mptr != NULL)
{
int index = find(mptr); //标记引用计数器的下表
if (index < 0)
{
Node tmp(mptr, 1); //没找到则进行创建引用计数器
node[cursize++] = tmp; //更新cuisize并添加引用计数器
//Node node;
//node[cursize].addr = mptr;
//node[cursize].ref = 1;
//cursize++;
}
else
{
node[index].ref++; //引用计数加一
}
/
//使用迭代器遍历
//std::vector<Node>::iterator fit = find(mptr);
//if (fit == vec.end())
//{
// Node node(mptr, 1);
// vec.push_back(node);
//}
//else
//{
// (*fit).ref++;
//}
/
}
}
void delref(void* mptr) //删除引用计数
{
if (mptr != NULL) //参数检查
{
int index = find(mptr); //标记下标
if (index < 0)
{
throw std::exception("addr is not exsit!"); //抛出异常
}
else
{
if (node[index].ref != 0) //判断引用计数是否为0
{
node[index].ref--; //引用计数减一
}
}
}
}
int getref(void* mptr) //获取引用计数
{
int rt = -1; //标记引用计数器的下标
if (mptr != NULL)
{
int index = find(mptr); //标记引用计数器的下表
if (index >= 0)
{
rt = node[index].ref;//
}
}
return rt;
}
private:
int find(void* mptr) //寻找引用计数
{
int rt = -1; //标记引用计数器的下标
for (int i = 0; i < cursize; i++)
{
if (node[i].addr == mptr)
{
rt = i;
break;
}
}
return rt;
/
//迭代器实现遍历
//std::vector<Node>::iterator it = vec.begin();
//for (it; it != vec.end(); it++)
//{
// if ((*it).addr == mptr)
// break;
//}
//return it;
/
}
private:
class Node
{
public:
Node(void* padd = NULL, int rf = 0) :addr(padd), ref(rf){}
public:
void* addr; //地址
int ref; //引用计数器
};
Node node[10]; //开辟十个引用计数器
int cursize; //下一个引用计数器的下表,也可以理解为引用计数
//器的个数;类比vector的实现中的cursize
private:
Ref_Management():cursize(0){} //构造函数私有化
Ref_Management(const Ref_Management&); //拷贝构造函数私有化
static Ref_Management rm; //用于生成唯一对象
};
Ref_Management Ref_Management::rm; //类外进行初始化
/
template<typename T>
class Shared_Ptr
{
public:
Shared_Ptr(T* ptr = NULL) :mptr(ptr) //构造函数
{
AddRef();
}
Shared_Ptr(const Shared_Ptr<T>& rhs) :mptr(rhs.mptr) //拷贝构造函数
{
AddRef();
}
Shared_Ptr<T>& operator=(const Shared_Ptr<T>& rhs) //赋值运算符重载函数
{
if (this != &rhs) //自赋值判断
{
this->~Shared_Ptr(); //调用析构函数释放旧的资源
mptr = rhs.mptr; //赋予新的资源
AddRef(); //添加引用计数
}
return *this;
}
~Shared_Ptr() //析构函数
{
DelRef(); //引用计数减一
if (GetRef() == 0) //判断引用计数是否为0
{
delete mptr; //真正的释放资源
}
mptr = NULL; //置空,防止野指针
}
T* operator->() // ->运算符重载函数
{
return mptr;
}
T& operator*() // *运算符重载函数
{
return *mptr;
}
///
private: //屏蔽接口
void AddRef()
{
prm->addref(mptr);
}
void DelRef()
{
prm->delref(mptr);
}
int GetRef()
{
return prm->getref(mptr);
}
private:
T* mptr; //用一块栈上的内存,管理堆上的无名字的内存
static Ref_Management* prm; //生成唯一对象
};
//静态成员变量类外进行初始化
template<typename T>
Ref_Management* Shared_Ptr<T>::prm = Ref_Management::getInstance();
缺点:share_ptr是强智能指针。交叉引用带来的问题:会造成内存泄漏,计数器会减少一次,但它本身被引用两次。这时我们发现,命名每个资源都只有一个对象来管理,却有两个引用计数,这也表示,在析构的时候会因为引用计数没有减到0而不释放其资源,造成内存泄漏。
智能指针之weak_ptr
weak_ptr智能指针的特点是:它是一个弱智能指针,不可单独使用,用于引用计数的管理,不能直接使用,需要和share_ptr配合使用。
代码示例如下:
template<typename T>
class Weak_ptr
{
public:
Weak_ptr(T* ptr = NULL) :mptr(ptr){} //构造函数
Weak_ptr(const Weak_ptr<T>& rhs) //带参数的构造函数
{
mptr = rhs.mptr;
}
Weak_ptr<T>& operator=(const Weak_ptr<T>& rhs) //Weak_ptr自己的赋值运算符重载函数
{
if (this != &rhs) //自赋值判断
{
mptr = rhs.mptr; //直接赋予新资源
}
return *this;
}
Weak_ptr<T>& operator=(const Shared_Ptr<T>& rhs)//传入Share_ptr智能指针的赋值运算符重载函数
{
mptr = rhs.getref();
return *this;
}
~Weak_ptr() //析构函数
{
}
T* operator->() // ->运算符重载函数
{
return mptr;
}
T& operator*() // *运算符重载函数
{
return *mptr;
}
private:
T* mptr;
};