c++ 智能指针 shared_ptr
创始人
2024-02-10 16:09:29

C++ 智能指针 shared_ptr 详解与示例_码农小明的博客-CSDN博客_shared_ptr

一、简介

        shared_ptr 是c++11的智能类,可以在任何地方都不使用的时候自动删除和相关指针,从而彻底消除内存泄漏和指针悬空的问题。

  她遵循共享所有权,即不同的shared_ptr 对象可以与相同的指针想关联

每个shared_ptr对象在内部纸箱两个内存位置:

1、指向对象的指针

2、用于控制引用计数数据的指针

二、shared_ptr 对象的基本操作

1、创建

    std::shared_ptr p1(new int());  // 有两个内存,1是存放int 的地址,2 是用来引用计数的内存地址(在哪里引用了) p1.use_count();  // 这个指针别引用了多长次

 2、创建空的shared_ptr对象

    std::shared_ptr  ptr1 = std::make_shared();

因为带有参数的 shared_ptr 构造函数是 explicit 类型的,所以不能像这样std::shared_ptr p1 = new int();隐式调用它构造函数。创建新的shared_ptr对象的最佳方法是使用std :: make_shared

这里可以说一下explicit  的所用:

explicit关键字用来修饰类的构造函数,被修饰的构造函数的类,不能发生相应的隐式类型转换,只能以显示的方式进行类型转换。

3、分离关联的原始指针

使用reset() 函数,但是这里有两种方式reset()和reset(params);

不带参数的reset()

p1.reset();  // 引用的计数会减一,如果计数为0 ,那么这个指针就会删除

带参数的reset():

p1.reset(new int(34)); // 将内部指向新的指针,因此其作用计算将变成1 

使用nullptr重置:

p1 = nullptr; 
if  1int main()
{//  std::shared_ptr p1(new int());  // 有两个内存,1是存放int 的地址,2 是用来引用计数的内存地址(在哪里引用了) //p1.use_count();  // 这个指针别引用了多长次// 创建一个空对象std::shared_ptr p1 = std::make_shared();std::cout << "p1  空 的引用次数  " << p1.use_count() << std::endl;*p1 = 3334;std::cout << "  p1=  " <<*p1 < p2(p1);std::cout << "  p2的内存地址  " << p2 << std::endl;  //// 下面两个输出都是:2std::cout << "p2 引用次数  = " << p2.use_count() << std::endl;std::cout << "p1 引用次数  = " << p1.use_count() << std::endl;// 比较智能指针,p1 等于 p2  ,由于p1  p2 的地址是一样的,所以p1==p2if (p1 == p2){std::cout << "p1 and p2 are pointing to same pointer\n";}std::cout << "Reset p1 " << std::endl;// 无参数调用reset,无关联指针,引用个数为0p1.reset();std::cout << "p1 Reference Count = " << p1.use_count() << std::endl;// 带参数调用reset,引用个数为1p1.reset(new int(11));std::cout << "p1  Reference Count = " << p1.use_count() << std::endl;// 把对象重置为NULL,引用计数为0p1 = nullptr;std::cout << "p1  Reference Count = " << p1.use_count() << std::endl;if (!p1) {std::cout << "p1 is NULL" << std::endl; // 输出}return  0;}
# endif

 4、自定义删除器 Deleter

delete Pointer;

当 shared_ptr 对象指向数组

// 需要添加自定义删除器的使用方式
std::shared_ptr p3(new int[12]);   // 仅用于演示自定义删除器// 指向数组的智能指针可以使用这种形式
std::shared_ptr p3(new int[12]);  // 正确使用方式

给shared_ptr添加自定义删除器

#if  1   // share_ptr 指针的使用struct A
{A(){std::cout << " A \n";}~A(){std::cout << " ~A \n";}
};void deleter(A* a)
{std::cout << "A   Deleter\n";delete[] a;
}int main()
{std::shared_ptrp4(new A[3], deleter);std::cout << "--------------------------------------------------------\n";//  std::shared_ptr p1(new int());  // 有两个内存,1是存放int 的地址,2 是用来引用计数的内存地址(在哪里引用了) //p1.use_count();  // 这个指针别引用了多长次// 创建一个空对象std::shared_ptr p1 = std::make_shared();std::cout << "p1  空 的引用次数  " << p1.use_count() << std::endl;*p1 = 3334;std::cout << "  p1=  " <<*p1 < p2(p1);std::cout << "  p2的内存地址  " << p2 << std::endl;  //// 下面两个输出都是:2std::cout << "p2 引用次数  = " << p2.use_count() << std::endl;std::cout << "p1 引用次数  = " << p1.use_count() << std::endl;// 比较智能指针,p1 等于 p2  ,由于p1  p2 的地址是一样的,所以p1==p2if (p1 == p2){std::cout << "p1 and p2 are pointing to same pointer\n";}std::cout << "Reset p1 " << std::endl;// 无参数调用reset,无关联指针,引用个数为0p1.reset();std::cout << "p1 Reference Count = " << p1.use_count() << std::endl;// 带参数调用reset,引用个数为1p1.reset(new int(11));std::cout << "p1  Reference Count = " << p1.use_count() << std::endl;// 把对象重置为NULL,引用计数为0p1 = nullptr;std::cout << "p1  Reference Count = " << p1.use_count() << std::endl;if (!p1) {std::cout << "p1 is NULL" << std::endl; // 输出}return  0;}
# endif

 三、shared_ptr 相对于普通指针的优缺点

缺少 ++, – – 和 [] 运算符

与普通指针相比,shared_ptr仅提供-> 、*==运算符,没有+-++--[]等运算符。

NULL检测

当我们创建 shared_ptr 对象而不分配任何值时,它就是空的;普通指针不分配空间的时候相当于一个野指针,指向垃圾空间,且无法判断指向的是否是有用数据。

 

创建 shared_ptr 时注意事项


不要使用同一个原始指针构造 shared_ptr
创建多个 shared_ptr 的正常方法是使用一个已存在的shared_ptr 进行创建,而不是使用同一个原始指针进行创建。
示例:

    int *num = new int(23);std::shared_ptr p1(num);std::shared_ptr p2(p1);  // 正确使用方法std::shared_ptr p3(num); // 不推荐std::cout << "p1 Reference = " << p1.use_count() << std::endl; // 输出 2std::cout << "p2 Reference = " << p2.use_count() << std::endl; // 输出 2std::cout << "p3 Reference = " << p3.use_count() << std::endl; // 输出 1


假如使用原始指针num创建了p1,又同样方法创建了p3,当p1超出作用域时会调用delete释放num内存,此时num成了悬空指针,当p3超出作用域再次delete的时候就可能会出错。

不要用栈中的指针构造 shared_ptr 对象
shared_ptr 默认的构造函数中使用的是delete来删除关联的指针,所以构造的时候也必须使用new出来的堆空间的指针。
示例:

int main()
{int x = 12;std::shared_ptr ptr(&x);return 0;
}


当 shared_ptr 对象超出作用域调用析构函数delete 指针&x时会出错。

建议使用 make_shared


为了避免以上两种情形,建议使用make_shared()<>创建 shared_ptr 对象,而不是使用默认构造函数创建。

std::shared_ptr ptr_1 = make_shared();
std::shared_ptr ptr_2 (ptr_1);


另外不建议使用get()函数获取 shared_ptr 关联的原始指针,因为如果在 shared_ptr 析构之前手动调用了delete函数,同样会导致类似的错误。

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...