1. 什么是智能指针?
智能指针是存储指向动态内存分配的,在类的构造是传入一个指针,在析构是释放指针。智能指针面对异常时格外有用,它们能够正确的销毁动分配的对象,
2.为什么要使用智能指针?
我们会经常遇到一下问题:
void fun(int *p)
{
*p = 5;
cout << *p << endl;
delete p;
}
int main()
{
int *q = new int;
fun(q);
cout << *q << endl;
return 0;
}
上面的代码出现的问题是:main函数申请了一片空间,但是在掉fun函数是把申请的空间释放了,这就导致main函数在后面打印*q时出错。
继续看下面的问题:
int main()
{
int *p = new int;
delete p;
return 0;
}
从代码角度看出main函数申请了一片内存,在后面也delete p将内存释放了,但是p指向的这片空间任然可以使用,这就是所谓的空悬指针(野指针),它会指向垃圾内存,给程序造成隐患。必须在后面将p置为NULL;
<1>. 智能指针可以帮助我们处理内存泄漏问题
<2>.智能指针可以帮助我们处理野指针的问题
<3>.智能指针可以帮我们处理比较隐晦的由异常造成的资源泄漏
事实上,智能指针还可以处理很多事情,例入处理线程安全,提供写时拷贝,确保协议等等。只能指针大部分用于生存期的控制,阶段控制。
3. 按照谁申请谁释放这一原则看以来完全欧克,但是当一个内存被多个函数使用是该由谁来是释放?
常见的只能指针:
<1>. shared_ptr强智能指针:基于引用计数的智能指针,它可以自由的共享它所指向的对象,可以随意赋值,直到这片内存的引用计数为0的时候才会被释放,也就是说在最后的一个对象销毁所有的资源内存。
<2>.weak_ptr弱智能指针:引用计数有一个问题,交叉引用形成环,使两个指针指向的内存都无法得到释放。需要手动打破循环引用或者使用弱指针weak_ptr。weak_ptr弱引用,只引用不计数。一片内存sheard_ptr和weak_ptr同时被引用,当所有sheard_ptr析构释放之后,不管有没有weak_ptr引用该内存,内存也会被释放。所以weak_ptr所指向的内存不能保证一定是有效的,在使用之前需要检查weak_ptr是否是空指针。
shared_ptr智能指针的实现
template <typename T>
class smartPtr
{
private:
T* _ptr;
static map<T*, int> _num;
public:
smartPtr(T *p)//构造函数
{
_ptr = p;
if (_num.find(p) != _num.end())
{
_num[p]++;
}
else
{
_num.insert(make_pair(p, 1));
}
cout << _ptr << ";" << _num[_ptr] << endl;
}
smartPtr(const smartPtr& src)//拷贝构造
{
_ptr = src._ptr;
_num[src._ptr]++;
}
~smartPtr()//析构函数
{
cout << "~smartPtr()" << endl;
if (--_num[_ptr] == 0)
{
delete _ptr;
}
cout << _ptr << ";" << _num[_ptr] << endl;
}
T& operator*()//重载运算符*
{
return *_ptr;
}
T* operator->()//重载运算符->
{
return _ptr;
}
};
template<typename T>
map<T*, int> smartPtr<T>::_num = map<T*, int>();
拷贝构造时,先找一下p这个指针有没有指向一片内存,如果有那么这片内存的引用计数+1,如果没有,则给p一片内存,这片内存的引用计数置为1。在析构的时候判断一下要析构的指针指向的内存的引用计数是否为0,若为0,则不释放,直到这片内存的引用计数为0才释放这片内存。
交叉引用及解决办法:
class B;
class A
{
public:
A()
{
cout << "A()" <<endl;
}
~A()
{
cout << "~A()" <<endl;
}
Shared_ptr<B> _ptrb;
};
class B
{
public:
B()
{
cout << "B()" <<endl;
}
~B()
{
cout << "~B()" <<endl;
}
Shared_ptr<A> _ptra;
};
int main()
{
shared_ptr<A> ptra(new A());
shared_ptr<B> ptrb(new B());
ptra->ptrb = ptrb;
ptrb->ptra = ptra;
return 0;
}
在这段代码中A类中有指向B类的shared_ptr指针,B类中也有指向A类的shared_ptr指针,A和B中的计数引用都为2,当main函数返回后,A和B的计数引用都变为1,所以内存不能释放,程序结束造成内存泄漏。
解决的办法:
在A和B类中使用sheard_ptr智能指针的地方全部换成weak_ptr智能指针。在使用强弱智能指针的时候有一个原则,就是只有在创建它的对象的时候才使用强智能指针,在其他地方想使用对象的时候都用弱智能指针。