——是一个类,用来存储指针(指向动态分配对象的指针)。
1.具有RAII思想
2.能够像指针一样(运算符重载,解引用,指向对象成员)
3.对资源进行封装和管理
RAII思想(资源分配及初始化)
1.定义一个类来封装资源的分配与释放,
2.构造函数中完成资源的分配及初始化;
3.析构函数中完成资源的清理,可以保证资源的正确初始化和释放
4.如果对象是用声明的方式在栈上创建局部对象,那么RAII机制就会正常工作,当离开作用域对象会自动销毁而调用析构函数释放资源。
代码分析:
void test()
{
int*_ptr=new int(1);
if(_ptr)
{
throw 1;
}
delete _ptr;
}
int main()
{
try
{
test();
}
catch(...)
{}
return 0;
}
上述代码所示,
1.在test函数中new一个四字节的空间,
2.判断if条件的语句为真,抛出异常
3.main函数直接catch 捕获异常,函数返回0
4.try 执行了直接执行catch,程序结束,以至于没有执行delete_ptr释放空间,导致内存泄漏。
其实在throw前加一个delete语句就可以解决问题,但是代码超级超级多的时候,如果有多个异常抛出,难道我们要写多个delete语句如此麻烦吗?
——智能指针 就可以解决这一问题
个人理解:
1.智能指针是通过基本类型(模板类)指针,构造类的对象,只能指针本身就是一个自定义的对象。
2.当此对象被销毁时,即调用此对象的析构函数,释放此指针。
也就是用栈中的空间来管理堆中的内存。
auto_ptr事实上是一个类,在构造对象时获取对象的管理权,
无需考虑释放动态内存开辟的空间,在析构函数中直接释放,不会出现内存泄漏的问题。
模拟实现:
//模拟实现auto_ptr
template
class Auto_ptr
{
public:
Auto_ptr(T*ptr)//构造函数
:_ptr(ptr)
{}
Auto_ptr(Auto_ptr&ap)//拷贝构造
:_ptr(ap._ptr)
{
ap._ptr = NULL;
}
Auto_ptr&operator=(Auto_ptr&ap)//赋值运算符的重载
{
if (this != &ap)
{
if (_ptr)
{
delete _ptr;
}
_ptr = ap._ptr;
ap._ptr = NULL;
}
return *this;
}
~Auto_ptr()//析构函数
{
if (_ptr)
{
delete _ptr;
}
}
T&operator*()
{
return *_ptr;
}
T*operator->()
{
return _ptr;
}
private:
T*_ptr;
};
struct AA
{
int _a;
int _b;
};
int main()
{
Auto_ptr ap(new int(3));
Auto_ptr ap1(ap);
Auto_ptr ap2(ap);//一个Auto_ptr被拷贝或赋值后,其已经失去了对原对象的所有权,指为NULL
Auto_ptrap3(new AA);
ap3->_a = 2;
ap3->_b = 3;
cout << &ap << endl;
cout << &ap1 << endl;
cout << &ap2 << endl;
system("pause");
return 0;
}
缺陷:
1.一个指针变量指向的空间不能由两个auto_ptr管理,不然会析构两次,使程序崩溃。(不推荐使用)
//错误
int*ptr=new int(1);
auto_ptr1ap(ptr);
auto_ptr2ap(ptr);
2.auto_ptr的拷贝构造,将源指针的管理权交给目标指针,会使得源指针悬空,解引用是会出现很多问题。
3.auto_ptr不能用来管理数组,析构函数中用的是delete
//错误
int *ptr=new int[6];
auto_ptrap(ptr);
scoped_ptr防拷贝,粗暴的方式(推荐使用)
1.拷贝构造函数和赋值运算符重载函数只声明不实现
2.用private对其进行访问限定,防止在类外定义
模拟实现:
//模拟实现scoped_ptr
template
class Scoped_ptr
{
public:
Scoped_ptr(T*ptr)//构造函数
:_ptr(ptr)
{}
~Scoped_ptr()
{
delete _ptr;
}
T&operator*()
{
return *_ptr;
}
T*operator->()
{
return _ptr;
}
private:
Scoped_ptr(const Scoped_ptr&sp);//拷贝构造
Scoped_ptr&operator=(const Scoped_ptr&sp);//赋值运算符的重载
T*_ptr;
};
int main()
{
Scoped_ptrsp(new int(2));
system("pause");
return 0;
}
缺陷:不能进行拷贝构造,管理的对象不能共享所有权,功能不全面。
scoped_array 和 scoped_ptr的功能是一样的,只是scoped_array管理的对象是数组,需要重载[]的形式。
模拟实现:
//模拟实现scoped_array
template
class Scoped_array
{
public:
Scoped_array(T*ptr)//构造函数
:_ptr(ptr)
{}
~Scoped_array()
{
delete[] _ptr;
}
T&operator[](size_t i)
{
return _ptr[i];
}
private:
Scoped_ptr(const Scoped_ptr&sa);//拷贝构造
Scoped_ptr&operator=(const Scoped_ptr&sa);//赋值运算符的重载
T*_ptr;
};
int main()
{
Scoped_arraysa(new int[2]);
system("pause");
return 0;
}
加入了引用计数,从而很好的规避了auto_ptr 释放两次空间,调两次析构的情况
模拟实现:
//模拟实现shared_ptr
template
class Shared_ptr
{
public:
Shared_ptr(T*ptr)//构造函数
:_ptr(ptr)
,_pCount(new int(1))
{}
Shared_ptr&(const Shared_ptr&sp)
: _ptr(sp._ptr)
; _pCount(sp._pCount)
{
(*_pCount)++;
}
Shared_ptr&operator=(Shared_ptr&sp)
{
if (this != sp)
{
if (--(*pCount) == 0)
{
delete _ptr;
delete _pCount;
}
_ptr = sp._ptr;
_pCount - sp._pCount;
(*_pCount)++;
}
return*this;
}
~Shared_ptr()
{
if (--(*pCount) == 0)
{
delete _ptr;
delete _pCount;
}
}
T&operator*()
{
return *_ptr;
}
T*operator->()
{
return _ptr;
}
int Count()
{
return *_pCount;
}
private:
T* _ptr;
int *_pCount;
};
shared_array 和 shared_ptr的功能是一样的,只是shared_array管理的对象是数组,需要重载[]的形式。
//模拟实现shared_ptr
template
class Shared_ptr
{
public:
Shared_ptr(T*ptr)//构造函数
:_ptr(ptr)
,_pCount(new int(1))
{}
Shared_ptr&(const Shared_ptr&sp)
: _ptr(sp._ptr)
; _pCount(sp._pCount)
{
(*_pCount)++;
}
Shared_ptr&operator=(Shared_ptr&sp)
{
if (this != sp)
{
if (--(*pCount) == 0)
{
delete[] _ptr;
delete _pCount;
}
_ptr = sp._ptr;
_pCount - sp._pCount;
(*_pCount)++;
}
return*this;
}
~Shared_ptr()
{
if (--(*pCount) == 0)
{
delete _ptr;
delete _pCount;
}
}
T&operator[]()
{
return _ptr[i];
}
int Count()
{
return *_pCount;
}
private:
T* _ptr;
int *_pCount;
};
但是,shared_ptr也有一个致命的缺点,就是会出现循环引用
Shared_ptr 会出现循环引用的情况:
//测试shared_ptr代码
struct LinkList
{
int _data;
Shared_ptr _next;
Shared_ptr _prev;
LinkList(int x)
:_data(x)
,_next(NULL)
,_prev(NULL)
{}
~LinkList()
{}
};
int main()
{
//定义两个对象(两个节点)
Shared_ptrcur(new LinkList(1));
Shared_ptrnext(new LinkList(2));
cur->_next = next; // next 类型是shared_ptr,在此处生成一个匿名对象(week_ptr 类型),再进行赋值
next->_prev = cur;
cout << "cur->pCount: " << cur.Count() << endl;
cout << "next->pCount: " << next.Count() << endl;
system("pause");
return 0;
}
//模拟实现shared_ptr
template
class Shared_ptr
{
public:
Shared_ptr(T*ptr)//构造函数
:_ptr(ptr)
,_pCount(new int(1))
{}
Shared_ptr(const Shared_ptr&sp)//拷贝构造
: _ptr(sp._ptr)
, _pCount(sp._pCount)
{
(*_pCount)++;
}
Shared_ptr&operator=(Shared_ptr&sp)//赋值运算符的重载
{
if (_ptr != sp._ptr)
{
if (--(*_pCount) == 0)
{
delete _ptr;
delete _pCount;
}
_ptr = sp._ptr;
_pCount = sp._pCount;
(*_pCount)++;
}
return *this;
}
~Shared_ptr()
{
if (--(*_pCount) == 0)
{
delete _ptr;
delete _pCount;
}
}
T&operator*()
{
return *_ptr;
}
T*operator->()
{
return _ptr;
}
int Count()
{
return *_pCount;
}
//protected:
T* _ptr;
int *_pCount;
};
template
class Week_ptr
{
public:
Week_ptr()
:_ptr(NULL)
{}
Week_ptr(const Shared_ptr &sp)
:_ptr(sp._ptr)
{}
T&operator*()
{
return *_ptr;
}
T*operator->()
{
return _ptr;
}
private:
T*_ptr;
};
//测试week_ptr
struct LinkList
{
int _data;
Week_ptr_next;
Week_ptr_prev;
LinkList(int x)
:_data(x)
,_next(NULL)
,_prev(NULL)
{}
~LinkList()
{}
};
int main()
{
//定义两个对象(两个节点)
Shared_ptrcur(new LinkList(3));
Shared_ptrnext(new LinkList(4));
cur->_next = next;
next->_prev = cur;
cout << "cur->pCount: " << cur.Count() << endl;
cout << "next->pCount: " << next.Count() << endl;
system("pause");
return 0;
}
补充说明week_ptr的工作原理: