在C++中动态分配内存可使用new和delete关键字,但是有时操作错误会导致内存泄漏,所以C++中开发了智能指针,智能指针的特点就是在使用后可以自己释放内存,避免内存泄漏的问题。
auto_ptr是在C++98中提出的智能指针概念,其本质是一个模板类,主要用于解决动态分配引起内存泄漏的问题。auto_ptr解决内存泄漏的方法是使用类的构造和析构函数,auto_ptr本质是一个模板类,分配内存时调用构造函数,指针使用完毕后调用析构函数释放资源,以此达到避免内存泄漏的目的。
auto_ptr作为智能指针具有很大的缺陷,主要体现在用auto_ptr赋值时会自动删除左操作数所指向的对象,例子:
auto_ptr ap1(new int(1024));
auto_ptr ap2;
ap2=ap1;
//将ap1赋值给ap2后,删除了ap2原来指的对象;ap2置为指向ap1所指的对象;ap1为未绑定对象。
并且auto_ptr还具有其他缺陷,所以在C++11中不再使用auto_ptr,换作使用shared_ptr和unique_ptr。
auto_ptr缺陷分析
Shared_ptr/unique_ptr和auto_ptr最大的区别就是,shared_ptr解决了指针间共享对象所有权的问题,也就是auto_ptr中的赋值的奇怪问题。所以满足了容器的要求,可以用于容器中。而auto_ptr显然禁止共享对象所有权,不可以用于容器中。
使用智能指针的头文件为。
shared_ptr和unique_ptr作为智能指针使用时与普通指针没什么区别,实现上与auto_ptr也没什么区别,都是调用类内构造和析构函数实现内存分配和释放。
unique_ptr可看作升级版的auto_ptr,而shared_ptr则支持多个智能指针指向同一个对象,并且在shared_ptr类内,有一个成员专门记录智能指针所指向对象同时被指向的智能指针个数。
//shared_ptr类内成员
template
class shared_ptr
{
private:
T *px; // contained pointer
unsignedlong* pn; // reference counter
}
类内成员px代表指针,pn代表被指对象同时对应的智能指针个数,当pn为0时,代表没有智能指针在指向对象,内存就会被释放。
其实shared_ptr的原理,就是使用px来记录指针,使用pn来记录px指向的对象的拥有者share_ptr的个数,当一个shared_ptr对象达到作用域时,不会释放资源,只有当pn变为0的时候,才会释放指针指向的资源。
因为shared_ptr本质上是一个类而不是标准指针,所以不能直接赋值,也不能使用标准指针对其赋值,只能用智能指针对智能指针赋值。
int* a=new int(2);
shared_ptrsp=a;// 不能使用指针直接赋值
sp=a;// 不能使用指针直接赋值
int* a=new int(2);
shared_ptr sp(a);//调用类内构造函数对智能指针赋值为a指向的对象
shared_ptr sp1(sp);//声明时使用智能指针给类内构造函数来对智能指针sp1赋值
sp1=sp;//用智能指针对智能指针直接赋值
Reset()函数:重置函数
reset()函数用于重置智能指针所指向的对象。当reset()函数无参数时,智能指针被重置为空指针,输出指向值为0。
//reset()函数
int* a=new int(2);
int* b=new int(3);
shared_ptr sp2(a);
shared_ptr sp1(a);
shared_ptr sp(a);
sp.reset(b);//用指针b重置智能指针sp
sp.reset(sp2); //用智能指针sp2重置智能指针sp
sp.reset();//使智能指针不再指向对象
使得sp获得b的拥有权。失去a的拥有权。注意这会使得a的拥有者少1.当a的拥有者变为0时,就会释放a的资源。
Swap()函数:交换函数
swap()函数用于将两个智能指针中的成员进行互换,进行swap()操作后,智能指针sp1和sp2中的px和pn都要互换,也就是不止互换指针指向对象,还互换对象同时共享指针数。
//swap()函数
int* a=new int(2);
shared_ptr sp(a);
shared_ptr sp1(a);
sp.swap(sp1);
Get()函数:返回px,即指向对象指针
Use_count函数:返回*pn,就是对象的拥有者的数量。
Unique函数:令*pn=1;让对象的拥有者的数量变为1,返回值为bool数。
shared_ptr智能指针中重载了运算符,‘==’和‘!=’分别代表智能指针指向对象是否相同,除此还重载了‘<’,‘*’和‘->'三个符号。
shared_ptr 本身不是 100%线程安全的。它的引用计数本身是安全且无锁的,但对象的读写则不是,因为shared_ptr有两个数据成员,读写操作不能原子化。根据文档,shared_ptr的线程安全级别和内建类型、标准库容器、string一样,即:
一个 shared_ptr 实体可被多个线程同时读取;
两个的 shared_ptr 实体可以被两个线程同时写入,“析构”算写操作;
如果要从多个线程读写同一个 shared_ptr 对象,那么需要加锁。
P.S. 这里可能用的是这两天刚看到的线程互斥锁pthread_mutex_lock
在定义shared_ptr 智能指针时还有一种方法,是多加一个参数,这个参数代表删除器,即一个代表对象引用数为0时会被调用的函数。定义时以带删除器的参数定义的智能指针在pn=0即对象没有对应指针时不再调用类内析构函数释放资源,而是调用删除器函数。如果在pn=0时不希望析构函数释放内存,可以定义带删除器的只能指针,这样在pn=0时会自动调用我们自己设定的删除器函数操作。
//定义带删除器的智能指针(源码)
template shared_ptr(Y * p, D d);
template void reset(Y * p, D d);
//删除器函数应用
void foo(int * d)
{
cout<<"1234"< sp(a,foo);//定义带删除器foo的智能指针sp
shared_ptr sp1(sp);
sp.reset();
sp1.reset();
//对象a的引用数为0了,这时不调用析构函数而是调用删除器foo,输出1234
system("pause");
return 0;
}
使用删除器时要注意:
指定的删除器的参数必须是int*;和shared_ptr中的int对应。不能是其他的,或者为空也是错的。因为系统会把shared_ptr的对象px赋给删除器的参数,我们也可以在删除器中释放资源。
只有a的引用次数为0才会调用,所以如果没有sp1.reset()。也不会调用foo函数。
P.S. 在使用shared_ptr智能指针时,定义初始化时需注意:
int* a=new int(2);
shared_ptr sp(a);//----------(1)
shared_ptr sp1(a);//---------(2)
这里的(2)也是不对的。想一想shared_ptr的构造函数,(1)的时候,sp的px指向a,且pn为1.而(2)的时候,px指向a,且pn也是1.这显然就问题了。a被引用了2次,但是*pn为1.在最后作用域达到的时候,就会释放2次内存,这就会引发异常。
shared_ptr智能指针总结