智能指针之shared_prt
为啥要有智能指针,因为要自己手动分配内存还要自己释放回收,太麻烦,万一忘记了还很容易造成内存泄漏。所以,类似与java内存托管的智能指针来了,C11很重要,使用频率也很高的一个特性。shared_ptr遵循共享资源,多个智能指针对象可以共享这一块资源,the wonderful world comes from sharing.
一、创建以及初始化
std::shared_ptr<int> p1; //创建一个int类型智能指针并且不赋值
std::shared_ptr<int> p2(new int (123));//创建一个int类型智能指针,同时new一个int的对象给它
std::shared_ptr<int> p3 = p1;//拷贝初始化,p3与p1指向相同的内容
std::shared_ptr<int> p4 = std::make_shared<int>();//采用make_shared初始化,在动态内存中创建一个对象给它
std::shared_ptr<int> p5 = std::make_shared<int>(456); //采用make_shared初始化,在动态内存中创建一个对象给它,并初始化为456
std::shared_ptr<int> p6;
p6.reset(new int(789));//采用智能指针成员函数reset进行初始化
建议还是采用make_shared的方式进行初始化比较安全,可以在动态存储区分配一个对象。
二、一些函数的操作
1.和unique_ptr共有操作 | |
---|---|
get() | a.get()//获取智能指针a的原型指针对象,但是在使用的时候要注意,一旦智能指针被析构,原型指针对象随之也消失 |
swap() | a.swap(b)//交换智能指针a与b中的原型指针 |
创建 | shared_ptr a; unique_ptr b//两种智能指针的创建 |
判断是否为空 | if(a){}//判断智能指针a是否为空 |
2.特有操作 | |
---|---|
use_count() | a.use_count()//智能指针a的引用计数 |
unique() | a.unique()//判断当前引用计数是否为1 |
make_shared(value) | 采用make_shared的方式在动态内存中分配一个T对象并用value进行初始化 |
shared_ptr p(q) | 拷贝初始化p |
make_shared使用规则:
shared_ptra = make_shared (value);//尖括号中是要创建的指针类型T,小括号中是要进行初始化的值
e.g:
shared_ptra = make_shared();//只创建int类型的智能指针且不进行初始化
shared_ptrb = make_shared(123);//创建int类型的智能指针并用初始化为123
shared_ptrc = make_shared(10,‘s’);//创建string类型的智能指针c并初始化为"ssssssssss"
三、对数组的管理
对数组的管理不同于普通的单独一个对象的托管,是一组对象的内存托管。通过一个例子来解释一下:
#include<iostream>
#include<string>
#include<memory>
class C
{
public:
C() :_value(""){};
C(string value) :_value(value){}
string get_value()const{
return _value;
}
~C(){
};
private:
string _value;
};
int main()
{
C *c = new C[2]{string("A"),string("B")};
shared_ptr<C> p(c);
system("pause");
return 0;
}
创建一个类,重写类的构造函数,创建一个类的实例为一维数组,运行起来,将断点放在类的析构位置,只能被析构一次,原因是智能指针只能对数组的第一个元素进行析构,所以无法实现对所有数组元素的析构,这时就需要自己写删除器进行析构,只举一个lambda的方式:
auto DeleterLambda=[](C*c){
close(c);
delete c;
};
shared_ptr<C> sp(new C("AAA"), DeleterLambda);
四、weak_ptr弱引用的智能指针
在介绍weak_ptr之前先分析一个问题:循环引用
class A
{
public:
A(){
shared_ptr<B> b;
};
~A();
private:
};
class B
{
public:
B(){
shared_ptr<A>a;
};
~B();
private:
};
int main()
{
shared_ptr<A> test1(new A);
shared_ptr<B> test1(new B);
system("pause");
return 0;
}
A引用了B,B引用了A,循环引用导致A/B都不会被析构,造成内存泄露问题。这也是在使用智能指针时要注意的问题。
下面我们再说一下weak_ptr,一种弱引用的智能指针,不会影响use_count(),可以理解为通过wea_ptr创建的智能指针为原来的副本,当通过shared_ptr创建的智能指针对象被析构的时候,weak_ptr的对象也相应消失。
基本用法:
1)use_count()获得当前观测资源的引用计数,且其不会应该引用计数
2)expired()判断当前观测西苑是否被释放
3)lock()获得当前所监视的智能指针,如果存在就创建一个副本如果不存在就创建一个当前智能指针同类型的空智能指针
五、使用智能指针的优点
1、 智能指针主要的用途就是方便资源的管理,自动释放没有指针引用的资源。
2、 使用引用计数来标识是否有多余指针指向该资源。(注意,shart_ptr本身指针会占1个引用)
3、 在赋值操作中, 原来资源的引用计数会减一,新指向的资源引用计数会加一。
std::shared_ptr p1(new Test);
std::shared_ptr p2(new Test);
p1 = p2;
4、 引用计数加一/减一操作是原子操作,所以线程安全的。
5、 make_shared要优于使用new,make_shared可以一次将需要内存分配好。
std::shared_ptr p = std::make_shared();
std::shared_ptr p(new Test);
6、 std::shared_ptr的大小是原始指针的两倍,因为它的内部有一个原始指针指向资源,同时有个指针指向引用计数。
(7) 引用计数是分配在动态分配的,std::shared_ptr支持拷贝,新的指针获可以获取前引用计数个数。
六、注意点
1、 不要用同一个原型指针初始化多个智能指针
int *p = new int(123);
shared_ptr ptr1 = make_shared§;
shared_ptr ptr2 = make_shared§;
如果ptr1销毁了托管对象的内存,则ptr2就会出错
2、不能在函数的实参中创建一个智能指针
编译器不同,编译时检索规则不同,有的从左到右,有的从右到左,会出现问题。
3、不能循环引用
前面的例子已经介绍过,不重复介绍
来源:CSDN
作者:Bussy-Yang
链接:https://blog.csdn.net/qq_39974998/article/details/104705794