shared_ptr 引用计数的增加和减少
共享式:引用计数,每一个 shared_ptr的拷贝都指向相同的内存(对象),只有最后一个指向该对象的shared_ptr指针不需要再指向该对象的时候,才会析构对象。
1.1引用计数的增加
每个shared_ptr都会记录有多少个其他的shared_ptr指向相同的对象
auto p6 = make_shared<int> (200);目前p6所指向的对象只有p6一个引用者
auto p7(p6); 智能指针定义的初始化,p7和p6指向了相同的对象,此对象目前有两个引用者
在如下情况下,所有指向这个对象的shared_ptr引用计数都会增加1:
a)像上边这样,我们用p6来初始化p7这个智能指针;
auto p7(p6);
b)把智能指针当做实参往函数里传递。
void myfunc(shared_ptr<int> ptmp)
{
return ;
}
myfunc(p7); 实参传递的时候,会复制到ptmp,会增加引用计数,出来的时候会减少1
void myfunc(shared_ptr<int> &ptmp)
{
return ;
}
myfunc(p7); 实参传递的是引用的时候,这样就不会出现引用计数的增加。
c)作为函数的返回值
shared_ptr<int> myfunc(shared_ptr<int> &ptmp)
{
return ;
}
auto p8 = myFunc(p7);
这里有p8来接,引用计数加一,
如果是:myFunc(p7); 引用计数就不会改变。
1.2引用计数的减少
shared_ptr<int> myfunc(shared_ptr<int> &ptmp)
{
return ;
}
auto p6 = make_shared<int> (200);
auto p7(p6);
auto p8 = myFunc(p7); 现在引用计数是3
a)给shared_ptr赋予新值,让该shared_ptr指向一个新对象
p8 = make_shared<int> (100); p8指向新对象,计数为1,p7,p6计数恢复为2
p7 = make_shared<int> (900); p7指向新对象,计数为1,p6指向的对象恢复为1
p6 = make_shared<int> (900); p6指向新对象,计数为1,p6指向的原内存被释放
b)局部的shared_ptr离开了作用域
就是上边那个传参的情况,进入之后加1,出来之后会被析构,计数减1;
c)当一个shared_ptr引用计数从1变成0,则它会自动释放自己所管理(指向)的对象。
auto pp = make_shared<int> (100); //只有pp指向该对象
auto pp2 = make_shared<int> (100);
pp = pp2;
给pp赋值会让pp指向pp2指向的对象,pp2对象引用计数变为2
原来的pp所指向的对象的引用计数变为0,从而被释放
shared_ptr指针的常用操作、删除器
(1)
use_count():返回多少个智能指针指向某个对象,主要用于调试的目的。
shared_ptr<int> myp(new int(100));
int icount = myp.use_count();//1
cout << icount << endl;
shared_ptr<int> myp2(myp);
icount = myp.use_count();//2
cout << icount << endl;
myp 和 myp2 都指向相同的内存,所以数量都相同。
(2)
unique():是否该智能指针独占某个指向的对象。
也就是若只有一个智能指针指向某个对象,则unique()返回true否则false
shared_ptr<int> myunique (new int(10));
if(myunique.unique())
{
cout << "unique ok" <<endl;
}
(3)
reset(): 恢复(复位、重置)的意思
(a): reset()不带参数的时候
若pi是唯一指向该对象的指针,那么释放pi所指向的对象,并将pi置空
pi.reset();
if(pi == nullptr)
{
cout << "pi被置空" <<endl;
}
若pi不是唯一指向该对象的指针,那么不释放pi所指向的对象,但指向该对象的引用计数会减少1,同时将pi指空
shared_ptr<int> pi(new int(100));
shared_ptr<int> pi2(new int(100));
pi = pi2;
int iCount = pi2.use_count();
cout << iCount << endl;//2
iCount = pi.use_count();
cout << iCount << endl;//2
pi.reset();
iCount = pi.use_count();//0
cout << iCount << endl;
iCount = pi2.use_count();//1
cout << iCount << endl;
(b) reset()带参数(一般是一个new出来的指针)时,
若pp是唯一指向该对象的指针,则释放pi指向的对象,让pi指向新对象
shared_ptr<int> pp(new int(100));
pp.reset (new int (1000));//释放原内存,指向新内存。
cout << *pp << endl;//1000
若pp不是唯一指向该对象的指针,则不释放pp指向的对象,但
指向该对象的引用计数会减少1,同时让pp指向新对象。
shared_ptr<int> pp(new int(100));
auto pp3(pp); //pp3 和 pp 的引用计数都是2
pp.reset(new int(200));
cout << pp3.unique() << endl;//1
cout << *pp << endl;//200
cout << *pp3 << endl;//100
//空指针也可以通过reset来重新初始化
shared_ptr<int> p;
p.reset (new int(11));//释放p所指向的对象,让p指向新对象
(4)
*解引用:获取p指向的对象。
shared_ptr<int> sptr(new int(100));
cout << *sptr << endl; // 打印100
(5)
get ();
返回p中保存的指针。小心使用,如果智能指针释放了所指向额对象
那么这个返回的裸指针也就变得失效。
存在的必要:考虑到有些函数(第三方函数)的参数需要是一个内置裸指针,而不是智能指针。
shared_ptr<int> getp (new int(122));
int *q = getp.get();
*q = 45;
cout << *getp <<endl;
//delete q; 尽量不要这样写,会产生不可预料的错误。
(6)
swap();
交换两个智能指针所指向的对象。
shared_ptr<int> swapp (new int(120));
shared_ptr<int> swapp2 (new int(122));
swap(swapp,swapp2);
cout << *swapp <<endl;//122
cout << *swapp2 <<endl;//120
swapp.swap(swapp2);
cout << *swapp <<endl;//120
cout << *swapp2 <<endl;//122
(7) = nullptr
a)将所指向的对象,引用计数减1,若引用计数变为0, 则释放智能指针所指向的对象。
b)将智能指针置空。
shared_ptr <string> sharptr (new string ("hello world"));
shared_ptr <string> sharptr2(sharptr);
cout <<sharptr.use_count() <<endl;//2
cout <<sharptr2.use_count() <<endl;//2
sharptr = nullptr ;
cout <<sharptr.use_count() <<endl;//0
cout <<sharptr2.use_count() <<endl;//1
(8)智能指针名字作为判断条件。
可以判断是否为空
shared_ptr <string> sharptr (new string ("hello world"));
if(sharptr)
{
cout << "智能指针不是空的" << endl;
}
else{
cout << "智能指针是空的" <<endl;
}
指定删除器以及数组问题
指定删除器:
#include <iostream>
#include <vector>
#include <memory> // shared_ptr
using namespace std;
void myDelete (int *p)//自己的删除器,删除整型指针用的,当智能指针引用计数为0
//就会自动调用来删除对象。
{
//写一些日志
cout << "这里被调用了"<<endl;
delete p;
}
int main()
{
一定时机帮我们删除所指向的对象,delete:将delete运算符作为默认的资源析构函数
我们可以指定自己的删除器取代系统提供的默认删除器
方法:一般只需要在参数中添加具体的删除函数名即可。
shared_ptr<int> pdelete (new int (1234) ,myDelete) ;
shared_ptr<int> pdelete2(pdelete);
pdelete.reset();
pdelete2.reset();
cout << pdelete.use_count() <<endl;
cout << pdelete2.use_count() <<endl;
有些情况默认删除器处理不了(用shared_ptr管理动态数组),需要我们 提供自己指定的删除器
return 0;
}
#include <iostream>
#include <memory> // shared_ptr
using namespace std;
int main()
{
删除器可以是lambda:表达式
shared_ptr<int> plambda(new int(123),[](int *p){
delete p;
});
有些情况默认删除器处理不了(用shared_ptr管理动态数组),需要我们 提供自己指定的删除器
shared_ptr<int> pArray (new int[12] ,[](int*p){
delete []p;
});
shared_ptr<A> pA(new A[12]); //异常因为系统释放pA是delete,pA应用delete []pA;
shared_ptr<A> pA(new A[12],[](A *p){
delete[]p;
});
}
default_delete
可用default_delete 来做删除器。default_delete 是标准库里的模板类
shared_ptr<A> pDefault (new A[12] ,default_delete<A[]>());
定义数组的时候,我们在尖括号里加[]
shared_ptr<A[]> pA_1(new A[10]);
shared_ptr<int[]> pA_2(new int[12]);
函数模板来封装shared_ptr 数组
#include <iostream>
#include <memory> // shared_ptr
using namespace std;
template <typename T>
shared_ptr<T> make_shared_array(size_t size)
{
return shared_ptr<T> (new T[size],default_delete <T[]> ());
}
class A
{
public:
A(){};
~A(){};
};
int main()
{
自己定义函数封装shared_ptr数组
shared_ptr<int> pintArray = make_shared_array<int>( 5 );
shared_ptr<A> pAAay = make_shared_array <A> (9);
return 0;
}
指定删除器额外声明:
#include <iostream>
#include <memory> // shared_ptr
using namespace std;
auto lambda1 = [](int *p)
{
//日志
delete p;
};
auto lambda_2 = [](int *q)
{
//日志
delete q;
};
class A
{
public:
A(){};
~A(){};
};
int main()
{
就算是shared_ptr指定了不同的删除器,只要他们指向的对象类型相同,那么这两个shared_ptr也属于同一个类型
shared_ptr<int> diffdelete(new int(122) ,lambda1);
shared_ptr<int> diffdelete2(new int(11),lambda_2);
diffdelete2 = diffdelete;
diffdlete2会先调用lambda2把自己指向的对象释放,然后指向diffdelete指向的对象,diffdelete指向的对象引用计数为2
整个main执行完毕之后,还会调用lambda1来释放diffdelete和diffdelete2共同指向的对象。
return 0;
}
类型相同,就代表可以放到元素类型为该对象类型的容器里
vector<shared_ptr<int>> pvec{diffdelete,diffdelete2};
make_shared 是提倡的生成shared_ptr的方法,但是make_shared这种方法,让我们没有方法指定自己的删除器。