一.为什么会有智能指针
智能指针其实是解决不经意间内存泄露的一个机制~
首先看下面的代码:
bool connectserver()
{
cout << "链接服务" << endl;
return rand() % 5 == 0;
}
void pushmail()
{
cout << "链接成功" << endl;
cout << "发送邮件" << endl;
if (rand() % 2 == 0)
{
throw 1;
}
}
void fun()
{
char *p= new char[1024 * 1024];
if (!connectserver())
{
cout << "链接数据库失败" << endl;
delete[]p;
return;
}
pushmail();
delete[]p;
}
int main()
{
while (1)
{
try
{
fun();
}
catch (int e)
{
cout << "抛出异常" << endl;
}
Sleep(1000);
}
system("pause");
return 0;
}
对于上述代码,执行过程乱跳,很容易造成内存泄露;
二.怎么解决呢?
首先要知道RAII:
如下解决代码
template<typename T>
class Auto_ptr
{
private:
T *_ptr;
public:
Auto_ptr(T *ptr)
:_ptr(ptr)//初始化获取资源
{}
~Auto_ptr()
{
delete[]_ptr;
_ptr = NULL;
}
};
bool connectserver()
{
cout << "链接服务" << endl;
return rand() % 5 == 0;
}
void pushmail()
{
cout << "链接成功" << endl;
cout << "发送邮件" << endl;
}
void fun()
{
Auto_ptr<char> ap1 = new char[1024 * 1024];
if (!connectserver())
{
cout << "链接数据库失败" << endl;
return;
}
pushmail();
}
int main()
{
while (1)
{
fun();
Sleep(1000);
}
system("pause");
return 0;
}
看如下代码
template<typename T>
class Auto_ptr
{
private:
T *_ptr;
public:
Auto_ptr(T *ptr)
:_ptr(ptr)//初始化获取资源
{}
~Auto_ptr()
{
delete[]_ptr;
_ptr = NULL;
}
};
这个存在的问题,既然是智能指针就需要有指针的功能,需要operator*和operator->
重载后的代码
template<typename T>
class Auto_ptr
{
private:
T *_ptr;
public:
Auto_ptr(T *ptr)//RAII不等于智能指针,智能指针只是RAII的一种思想的实现
:_ptr(ptr)//初始化获取资源
{}
~Auto_ptr()
{
delete[]_ptr;
_ptr = NULL;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
};
除了重载那两个符号的问题还有什么问题?
void test()
{
int *p = new int(10);
Auto_ptr<int> ap1(p);
Auto_ptr<int> ap2(ap1);
}
程序崩溃,因为其指向一块内存,注意和string的区别,智能指针只是帮忙管理,不进行操作的
通过的初始化崩溃问题引出
c98之前人们解决的方案是-----》权限管理,但是其也是带有严重的缺陷的解决问题
接下来给出权限转移的代码,这就需要我们自己来写拷贝构造函数了,代码如下
template<typename T>
class Auto_ptr
{
private:
T *_ptr;
public:
Auto_ptr(T *ptr)//RAII不等于智能指针,智能指针只是RAII的一种思想的实现
:_ptr(ptr)//初始化获取资源
{}
~Auto_ptr()
{
delete[]_ptr;
_ptr = NULL;
}
T& operator*()
{
return *_ptr;
}
T operator->()
{
return _ptr;
}
Auto_ptr(Auto_ptr<T>& p)//拷贝构造函数,拷贝完就释放原来的空间~
:_ptr(p._ptr)
{
p._ptr = NULL;
}
};
除此之外还有赋值运算符重载函数,不然赋值的时候还会崩溃
代码如下
template<typename T>
class Auto_ptr
{
private:
T *_ptr;
public:
Auto_ptr(T *ptr)//RAII不等于智能指针,智能指针只是RAII的一种思想的实现
:_ptr(ptr)//初始化获取资源
{}
~Auto_ptr()
{
delete[]_ptr;
_ptr = NULL;
}
T& operator*()
{
return *_ptr;
}
T operator->()
{
return _ptr;
}
Auto_ptr(Auto_ptr<T>& p)//拷贝构造函数,拷贝完就释放原来的空间~
:_ptr(p._ptr)
{
p._ptr = NULL;
}
Auto_ptr<T>& operator=(Auto_ptr<T>& ap)
{
if (this != &ap)
{
if (_ptr)//先释放ap2空间
{
delete _ptr;
}
_ptr = ap._ptr;
ap._ptr = NULL;
}
return *this;
}
};
void test()
{
int *p = new int(10);
Auto_ptr<int> ap1(p);
Auto_ptr<int> ap2(ap1);
ap2 = ap1;
}
这就是autoptr,尽管如此解决了初始化和赋值的问题,但是比如ap2=ap1,ap1这块空间就不能使用了,还是有严重缺陷的,但是为什么还要知道他呢,因为他还在我们的库里面,C++一旦产生的语法就不能取消,这就是向前兼容
上面的不好用怎么办呢?因为实在太烂了,所以有人就会造轮子,就出现了boost,他不是C++的标准库但是是比较重要的一个库
他产生了自己一套智指针
其实C++还有自己的几种智能指针
1.unique_ptr
2.shared_ptr
weak_ptr他其实是参考了boost
对上图中的代码做一些解释
scoped简单粗暴,防拷贝的设计
shared引用计数功能更全比较复杂,效率相对会比较低,还有循环引用的缺陷,用weak ptr去解决
1.首先来说名scoped ptr
template<class T>
class ScopdePtr
{
public:
//raii
ScopdePtr(T *ptr) :_ptr(ptr){}
~ScopdePtr()
{
delete _ptr;
}
//智能指针的特性
T& operator*()
{
return *_ptr;
}
T operator->()
{
return *_ptr;
}
private://注意声明为私有的
ScopdePtr(ScopdePtr<T>&);//放拷贝,只声明不定义,就无法调用拷贝哦
ScopdePtr<T> operator=(ScopdePtr<T>&);
private:
T *_ptr;
//解决拷贝的问题,防拷贝
};
void testScopdePtr()
{
ScopdePtr<int> sp1(new int(10));
ScopdePtr<int> sp2(sp1);
}
int main()
{
testScopdePtr();
return 0;
}
2.scoped array
注意这个里面的operator*和operator->就没什么用了
template<class T>
class ScopdeArray
{
public:
//raii
ScopdeArray(T *ptr) :_ptr(ptr){}
~ScopdeArray()
{
delete [] _ptr;
}
//智能指针的特性
/*T& operator*()
{
return *_ptr;
}
T operator->()
{
return *_ptr;
}*/
T &operator[](size_t pos)
{
return _ptr[pos];
}
private://注意声明为私有的
ScopdeArray(ScopdeArray<T>&);//放拷贝,只声明不定义,就无法调用拷贝哦
ScopdeArray<T> operator=(ScopdeArray<T>&);
private:
T *_ptr;
//解决拷贝的问题,防拷贝
};
void testScopdeArray()
{
ScopdeArray<int> sp1(new int(10));
ScopdeArray<int> sp2(sp1);
}
int main()
{
testScopdeArray();
return 0;
}
3.shared ptr
template<class T>
class SharedPtr
{
public:
SharedPtr(T *ptr)
:_ptr(ptr)
, refCount(new int(1)){}
~SharedPtr()
{
if (--(*refCount) == 0)
{
delete _ptr;
delete refCount;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
SharedPtr(SharedPtr<T> &sp)
:_ptr(sp._ptr)
, refCount(sp.refCount)
{
++(*refCount);
}
SharedPtr& operator=(const SharedPtr<T> &sp)
{
if (_ptr != sp._ptr)
{
if (--(*refCount) == 0)
{
delete _ptr;
delete refCount;
}
_ptr = sp._ptr;
refCount = sp.refCount;
++(*refCount);
}
}
private:
T *_ptr;
int *refCount;
};
void testSharedPtr()
{
SharedPtr<int > sp1(new int(10));
SharedPtr<int> sp2(sp1);
}
int main()
{
testSharedPtr();
return 0;
}
4.shared array
template<class T>
class SharedArray
{
public:
SharedArray(T *ptr)
:_ptr(ptr)
, refCount(new int(1)){}
void release()
{
if (--(*refCount) == 0)
{
delete[] _ptr;
delete refCount;
}
}
~SharedArray()
{
release();
}
SharedArray(SharedArray<T> &sp)
:_ptr(sp._ptr)
, refCount(sp.refCount)
{
++(*refCount);
}
SharedArray& operator=(const SharedArray<T> &sp)
{
if (_ptr != sp._ptr)
{
release();
_ptr = sp._ptr;
refCount = sp.refCount;
++(*refCount);
}
return *this;
}
T& operator[](size_t pos)
{
return _ptr[pos];
}
private:
T *_ptr;
int *refCount;
};
5.shared中的循环引用
看如下代码:
template<class T>
class SharedPtr
{
public:
SharedPtr(T *ptr=NULL)
:_ptr(ptr)
, refCount(new int(1)){}
void release()
{
if (--(*refCount) == 0)
{
if (_ptr == NULL)
return;
printf("0x%p\n", _ptr);
delete _ptr;
delete refCount;
}
}
~SharedPtr()
{
release();
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
SharedPtr(SharedPtr<T> &sp)
:_ptr(sp._ptr)
, refCount(sp.refCount)
{
++(*refCount);
}
SharedPtr<T>& operator=(const SharedPtr<T> &sp)
{
if (_ptr != sp._ptr)
{
release();
_ptr = sp._ptr;
refCount = sp.refCount;
++(*refCount);
}
return *this;
}
private:
T *_ptr;
int *refCount;
};
template<class T>
class weakptr
{
public:
weakptr() :_ptr(NULL)
{}
weakptr(const SharedPtr<T>& sp) :_ptr(sp._ptr){}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T *_ptr;
};
struct ListNode
{
SharedPtr<ListNode> _perv;
SharedPtr<ListNode> _next;
};
void testcycle()
{
SharedPtr<ListNode> cur = new ListNode;
SharedPtr<ListNode> next = new ListNode;
cur->_next = next;
next->_perv = cur;
}
int main()
{
testcycle();
system("pause");
return 0;
}
重点看这段代码:
struct ListNode
{
SharedPtr<ListNode> _perv;
SharedPtr<ListNode> _next;
};
void testcycle()
{
SharedPtr<ListNode> cur = new ListNode;
SharedPtr<ListNode> next = new ListNode;
cur->_next= next;
next->_perv= cur;
}
int main()
{
testcycle();
system("pause");
return 0;
}
这里就产生了循环引用
在这注意:
内存并未释放
解决方法就是用weakptr
代码如下:
template<class T>
class weakptr
{
public:
weakptr() :_ptr(NULL)
{}
weakptr(const SharedPtr<T>& sp) :_ptr(sp._ptr){}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T *_ptr;
};
其作用只是托管指针空间不进行操作
用法如下代码
template<class T>
class weakptr;
template<class T>
class SharedPtr
{
public:
friend class weakptr<T>;
SharedPtr(T *ptr=NULL)
:_ptr(ptr)
, refCount(new int(1)){}
void release()
{
if (--(*refCount) == 0)
{
if (_ptr == NULL)
return;
printf("0x%p\n", _ptr);
delete _ptr;
delete refCount;
}
}
~SharedPtr()
{
release();
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
SharedPtr(SharedPtr<T> &sp)
:_ptr(sp._ptr)
, refCount(sp.refCount)
{
++(*refCount);
}
SharedPtr<T>& operator=(const SharedPtr<T> &sp)
{
if (_ptr != sp._ptr)
{
release();
_ptr = sp._ptr;
refCount = sp.refCount;
++(*refCount);
}
return *this;
}
private:
T *_ptr;
int *refCount;
};
template<class T>
class weakptr
{
public:
weakptr() :_ptr(NULL)
{}
weakptr(const SharedPtr<T>& sp) :_ptr(sp._ptr){}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T *_ptr;
};
struct ListNode
{
weakptr<ListNode> _perv;
weakptr<ListNode> _next;
};
void testcycle()
{
SharedPtr<ListNode> cur = new ListNode;
SharedPtr<ListNode> next = new ListNode;
cur->_next = next;
next->_perv = cur;
}
int main()
{
testcycle();
system("pause");
return 0;
}
这样就释放了,解决了循环引用的问题。