1.单个对象
shared_ptr<int>pa = make_shared<int>(100);//pa指向的内存初始化为100
//或share_ptrpa(new int(100));
//该内存的strong ref = 1
shared_ptr<int>pb(pa); //pb与pa指向同一块内存
//此时,有两个shared_ptr对象指向该内存,故该内存的strong ref 变为2
2.数组
class A
{
public:
A(){} //构造函数
~A(){} //析构函数
//...
};
int maint(void)
{
//正确的写法,会调用会调用每个A对象的构造函数和析构函数
shared_ptr<A[]>Aarr(new A[10]); //创建10个A对象
//错误的写法:程序结束时不会调用所以数组里的对象的析构函数,引发异常
//shared_ptrAarr(new A[10]);
//另一个正确的写法:
shared_ptr<A>arrA(new A[10], default_delete<A[]>());
}
cout<<sizeof(pa)<<endl; //8 [win 32]
我们在win 32上打印普通指针的大小为4,比如:
int* p; //一个普通指针
cout<<sizeof(p)<<endl; //4
那么为什么shared_ptr对象的大小就是8呢?
接下来我们把普通指针称为裸指针
这是因为,shared_ptr不仅存有一个裸指针指向一个内存,而且该裸指针指向的内存也有一些信息需要share_ptr的一个指针去指向。
如下图:
在上面的代码中,pa和pb里指向数据的指针都指向了中间的同一个数据内存块,该数据内存块相关的信息内存块也因pb指向该数据内存块而发生变化,比如信息内存里的strong ref增加1,表示又有一个shared_ptr指向该数据内存块
1.use_count
判断有几个share_ptr指向同一块内存【数据块和相关信息块】
unique() 判断是否只有一个share_ptr指向该内存,返回1:是;返回0:否
shared_ptr<int>pa = make_shared<int>(100);
shared_ptr<int>pb(pa);
cout<<pa.use_count()<<endl; //2
cout<<pb.use_count()<<endl; //也是2
if(pa.unique())
cout<<"unique"<<endl;
else
cout<<"not unique"<<endl;
//将打印not unique
2.reset()
这个函数干了这几件事:①该shared_ptr对象指向的内存块的信息中strong ref减1 ②如果该shared_ptr对象指向的内块信息的strong ref变为0,则清空该内存块 ③清空该shared_ptr对象
shared_ptr<int>pa = make_shared<int>(100); //strong ref = 1
shared_ptr<int>pb(pa); //strong ref = 2
pa.reset(); //strong ref = 1,pa被清空,但内存还在
cout<<pb.unique()<<endl; //1
pb.reset(); //strong ref = 0,该内存被释放,pb也被清空
//pb = nullptr;//也可以用这个语句来代替pb.reset()
3.get()
返回该shared_ptr对象的裸指针,也就是返回下图中橙色的这块
shared_ptr<int>pa = make_shared<int>(100);
cout<<*pa<<endl; //100
int *nakedP = pa.get();
cout<<*nakedP<<endl; //100
//千万别这么干: delete nakedP;//引发异常
4.swap
shared_ptr<string>pa = make_shared<string>("Rose");
shared_ptr<string>pb = make_shared<string>("Jack");
pa.swap(pb); //两者指向的内存对换
swap(pa,pb); //现在又换回来了
shared_ptr指向的内存会在程序结束后自动释放,或者在strong ref=0时释放。这些都是缺省的释放方法,我们也可以在创建shared_ptr对象时定义自己的释放函数(删除器),比如在释放时简单地打印一条信息
1.
`shared_ptr<int>pa (new int(100), myDelete); //myDelete是自己定义的函数,注意这里只能用new,不能用make_shared
`
myDelete定义如下:
void myDelete(int *p)
{
cout<<"myDelete执行了"<<endl;
delete p;
}
2.也可以用lambda表达式代替
make_shared<int>pa(new int(100), [](int *p)
{
cout<<"lambda表达式删除器"<<endl;
delete p;
});
shared_ptr<int>pa = make_shared<int>(100);
shared_ptr<int>pb(move(pa)); //清空pa。由pb指向该内存
1.不要用同一个裸指针初始化多个shared_ptr对象
下面是一段引发异常的代码
int* p = new int(100);
shared_ptr<int>p1(p);
shared_ptr<int>p2(p);
在上面代码中,如果在创建p1后立刻调用p1.reset(); 那么就不会引发异常
2.避免套娃
有两个类,CA和CB
class CB;
class CA
{
public:
~CA()
{
cout << "~CA()" << endl;
}
shared_ptr<CB> mpb;
};
class CB
{
public:
~CB()
{
cout << "~CB()" << endl;
}
shared_ptr<CA> mpa;
};
在main()中
这么使用这两个类会导致无法调用这两个类的析构函数,引发内存泄露
shared_ptr<CA>a(make_shared<CA>());
shared_ptr<CB>b(make_shared<CB>());
a->mpb = b;
b->mpa = a;
cout << a.use_count() << endl;
cout << b.use_count() << endl;
//要加下面这两行才可以调用析构函数
//a->mpb.reset();
//b->mpa.reset();
分析:a和b两个类类型的share_ptr,要等到各自指向内存的strong ref变为0时才能被释放,执行析构函数,而a的成员mpb和b的成员mpa都要由析构函数来释放,这样就造成了“死锁”。所以,如果我们显式地调用最后两个类对象分别的mpb和mpa成员,让strong ref都降为1,才可以指向该类的析构函数。
weak_ptr可以用来辅助shared_ptr的使用,怎么辅助呢,请接着往下阅读:
1.lock() 和 use_count()以及expired()
1)lock(): 先用一个shared_ptr来初始化一个weak_ptr对象,那么该weak_ptr对象的一个指针也指向了下图中蓝色的区域:
该weak_ptr调用lock函数,对应内存的strong ref >=1,则返回一个指向该内存的shared_ptr对象,若对应内存的strong ref =0,则返回一个空的share_ptr
2)use_count()返回指向对应内存的shared_ptr对象的数量
3)expired():如果没有shared_ptr指向该内存,则返回1,若有则返回0
shared_ptr<int>p = make_shared<int>(100);
weak_ptr<int>weak_p1(p); //strong ref不因weak_p改变,但weak ref增1
cout << sizeof(weak_p1) << endl; //8
//weak_ptr的lock()函数:weak_ptr 指向的shared_ptr对象存在,则返回该shared_ptr对象且strong ref增1,否则,返回一个空的share_ptr,strong ref不增
auto pret = weak_p1.lock();
if (pret != nullptr)
{
cout << "weak_p1指向的对象存在" << endl;
}
cout << weak_p1.use_count() << endl; //获得strong ref 数 2
p.reset();
pret.reset();
cout << weak_p1.expired() << endl; //1 判断指向的对象是否存在
weak_p1.reset(); //weak ref 置0,清空weak_p1,不影响strong ref
1.普通的对象
unique_ptr<int>p1 = make_unique<int>(100); //声明并初始化为100
unique_ptr<int>p2(new int(90)); //初始化为90
unique_ptr<string>p3 = make_unique<string>("OKOK"); //初始化为“OKOK”
unique_ptr<string>p4(new string("YESYES")); //初始化为“YESYES”
2.指向数组的对象
unique_ptr<int[]>arrP(new int[5]);
arrP[0] = 99;
cout << arrP[0] << endl; //99
3.自定义删除器的对象
1)函数删除器
定义一个删除器函数如下:
void myDelete(int* p)
{
delete p;
cout<<"调用了myDelete"<<endl;
}
在main()中:
using fp = void(*)(int*);
//或 typedef void(*fp)(int*);
//或 typedef decltype(myDelete) (*fp);
unique_ptr<int,fp>pa(new int(15),myDelete);
2)lambda表达式删除器
auto myLambda = [](int *p){
cout<<"myLambda删除器"<<endl;
delete p;
};
unique_ptr<int,decltype(myLambda)> pb (new int(16),myLambda);
1.删除器会影响unique_ptr对象的大小
unique_ptr<int>p1 = make_unique<int>(99);
unique_ptr<int,fp>pa(new int(15),myDelete); //mydelete定义在上文
unique_ptr<int,decltype(myLambda)> pb (new int(16),myLambda);//myLambda定义在上文
cout<<sizeof(p1)<<endl; //4
cout<<sizeof(pa)<<endl; //8
cout<<sizeof(pb)<<endl; //4
reset():释放该unique_ptr对象指向的内存,并置空该unique_ptr对象
reset(裸指针参数):解除该uniuqe_ptr与原来指向内存的关系,清空原来指向的内存,并重新指向裸指针参数指向的内存
release():返回一个裸指针,并解除该内存与该unique_ptr对象的联系,置空该unique_ptr对象
unique_ptr<int>p1 = make_unique<int>(10);
unique_ptr<int>p2= make_unique<int>(20);
p1.reset(new int(1)); //清空p1原来指向存放10的内存,置空p1
//让p1指向新内存,新内存存着1
p1.reset(); //清空p1指向的存着1的内存,置空p1
p1.reset(p2.release()); //p2.release()返回指向存着20的内存的裸指针,置空p2。p1指向该裸指针指向的内存
1.移动语义
unique_ptr<int>p1 = make_unique<int>(33);
unique_ptr<int>p2(move(p1));
//现在p1被置空,p2指向存着33的内存
2.函数返回unique_ptr
uniuqe_ptr对象指向的内存不可被多个unique_ptr对象共享,但是可以拷贝即将要被销毁的unique_ptr,比如接受函数返回,如下:
定义一个函数:
unique_ptr<int> myFunc()
{
return make_unique<int>(100);
}
在main()中
unique_ptr<int>p = myFunc();
cout<<*p<<endl; //100
1.不同删除器的uniuqe_ptr不可相互swap或装入通一个容器