理解C++智能指针
来源:互联网 发布:不常用的英文单词知乎 编辑:程序博客网 时间:2024/06/07 01:03
C++智能指针是面试中经常会问到的一个经典知识点,本身使用也具有很大的意义。本文从下面三个方面对智能指针的内容进行整理,以期对智能指针能够有一个较为清晰的认识:
1 智能指针的实现原理
2 常用的智能指针
3 智能指针的实现
1、智能指针的实现原理:
智能指针是一个类,且这个类是个模板类,为了适应不同基本类型的需求,它在构造函数中传入一个普通指针,将这个基本类型指针封装为类对象指针,并在析构函数中释放这个指针,删除该指针指向的内存空间。因为一般使用时,智能指针的类都是局部对象,所以当函数(或程序)自动结束时会自动被释放。
2、 常用的智能指针:
STL一共提供了四种智能指针:auto_ptr,unique_ptr,shared_ptr和weak_ptr
对于所有的智能指针需要注意以下几点:
1) 所有的智能指针类都有一个explicit构造函数(阻止不应该允许的经过转换构造函数进行的隐式转换的发生,即不能在隐式转换中使用),因此不能直接将指针转换为只能指针对象,必须显式调用,即
int* test=new int(2);std::shared_ptr<int> p1=test; //编译报错,无法从“int *”转换 //为“std::tr1::shared_ptr<_Ty>” //因为构造函数被声明为explicit,必须显式调用std::shared_ptr<int> p2(test); //正确,显式调用
2) 对全部三种智能指针都应避免的一点:
string vacation("hello,world"); shared_ptr<string> pvac(&vacation); // 错误,No pvac过期时,程序将把delete运算符用于非堆内存,导致错误shared_ptr<string> pvac1(new string("hello,world")); //正确
下面简要说明四种智能指针的特点:
1)std::auto_ptr:属于独占内存的方式,当p1=p2;时,p2的内存使用权转移给p1(p1指向p2之前所指向的地址),p2成为空悬指针(指针地址为0),若之后使用p2,可以编译通过,但运行时会出现内存访问错误,不安全,会出现内存崩溃的问题。也因此不能被放入容器中(C++11已将其摒弃)
int* test=new int(2); std::auto_ptr<int> p1(new int(5)); printf("p1:%d\n",p1); std::auto_ptr<int> p2(test); std::auto_ptr<int> p3=p1; //不报编译错误 printf("p3:%d\n",p3); printf("p1:%d\n",p1); //std::auto_ptr<int> p4(p2); //运行时报访问错误 //printf("%d\n",*p1); //报访问错误,因为p1将内存管理权转移给p3了,p1悬空 printf("%d\n",*p2);
2)C++引入的unique_ptr,也属于独享内存所有权,但优于auto_ptr,拷贝构造函数和赋值函数只有声明没有定义,且为私有函数,不能使用。直接赋值会编译出错。需要赋值的时候用std::move
std::unique_ptr<int> p5(new int(8)); std::unique_ptr<int> p6(test); printf("%d\n",*p5); printf("%d\n",*p6); //std::unique_ptr<int> p7(p5); //编译报错,库内为private成员,且只声明,未定义 //std::unique_ptr<int> p8=p6; //编译报错,库内为private成员,且只声明,未定义 //printf("%d\n",*p7); //printf("%d\n",*p8); std::unique_ptr<int> p9=unique_ptr<int>(test); printf("%d\n",*p9); printf("p9:%d\n",p9); p6=std::move(p9); printf("p6:%d\n",p6); printf("p9:%d\n",p9); //printf("%d\n",*p6); //内存访问出错,因为p6转移了内存所有权
3)shared_ptr(boost、C++11)属于共享内存,内部有引用计数机制(实现方式有两种,一种是辅助类,一种是句柄类),对一个内存对象进行引用计数,当删除其中一个指向该内存的指针时,引用计数减1,但并不会释放该内存对象,只有当该内存对象的引用计数减为0时,才会释放该块内存,避免了指针空悬、内存访问错误的情况。
std::shared_ptr<int> p10(new int(10)); std::shared_ptr<int> p11(test); printf("%d\n",*p10); printf("%d\n",*p11); printf("p11->use_count:%d\n",p11.use_count()); std::shared_ptr<int> p12(p10); //编译正常 std::shared_ptr<int> p13=p11; //编译正常 //std::weak_ptr<int> p13=p11; //编译正常 printf("p11->use_count:%d\n",p11.use_count()); printf("p13->use_count:%d\n",p13.use_count()); printf("p11:%d\n",p11); printf("p13:%d\n",p13); //p11=p13; //编译正常 printf("p11->use_count:%d\n",p11.use_count()); printf("p13->use_count:%d\n",p13.use_count()); printf("p11:%d\n",p11); printf("p13:%d\n",p13); //std::shared_ptr<int> p13=test; //编译报错,无法从“int *”转换为“std::tr1::shared_ptr<_Ty>”,因为构造函数被声明为explicit,必须显式调用 printf("%d\n",*p11); printf("%d\n",*p12); //printf("%d\n",*p13); std::shared_ptr<int> p14=std::move(p10); printf("%d\n",*p14); p10 = p14; printf("%d\n",*p10); //内存访问出错,因为p10转移了内存所有权 string vacation("I wandered lonely as a cloud."); shared_ptr<string> pvac(&vacation); // No cout<<*pvac;
4)weak_ptr(boost、C++11)为shared_ptr设计,弱引用。只对shared_ptr进行引用,但不改变其计数;所以,当被引用的shared_ptr失效后,相应的weak_ptr也会失效。测试代码如下:
std::shared_ptr<int> p10(new int(10)); std::shared_ptr<int> p11(test); printf("%d\n",*p10); printf("%d\n",*p11); printf("p11->use_count:%d\n",p11.use_count()); std::shared_ptr<int> p12(p10); //编译正常 //std::shared_ptr<int> p13=p11; //编译正常 std::weak_ptr<int> p13=p11; //编译正常 printf("p11->use_count:%d\n",p11.use_count()); printf("p13->use_count:%d\n",p13.use_count()); printf("p11:%d\n",p11); printf("p13:%d\n",p13); //p11=p13; //编译正常 printf("p11->use_count:%d\n",p11.use_count()); printf("p13->use_count:%d\n",p13.use_count()); printf("p11:%d\n",p11); printf("p13:%d\n",p13); //std::shared_ptr<int> p13=test; //编译报错,无法从“int *”转换为“std::tr1::shared_ptr<_Ty>”,因为构造函数被声明为explicit,必须显式调用 printf("%d\n",*p11); printf("%d\n",*p12); //printf("%d\n",*p13); std::shared_ptr<int> p14=std::move(p10); printf("%d\n",*p14); p10 = p14; printf("%d\n",*p10); //内存访问出错,因为p10转移了内存所有权 string vacation("I wandered lonely as a cloud."); shared_ptr<string> pvac(&vacation); // No cout<<*pvac;
看起来weak_ptr没有什么实质的作用,但实际上weak_ptr 主要用在软件架构设计中,可以在基类(此处的基类并非抽象基类,而是指继承于抽象基类的虚基类)中定义一个 boost::weak_ptr,用于指向子类的boost::shared_ptr,这样基类仅仅观察自己的 boost::weak_ptr 是否为空就知道子类有没对自己赋值了,而不用影响子类 boost::shared_ptr 的引用计数,用以降低复杂度,更好的管理对象
3、 智能指针的实现
这里采用上文提到的辅助类的方式,具体代码如下:
//辅助类,用来引用计数template<class friendclass,class T>class U_ptr { friend friendclass; T *_p; int use_count; U_ptr(T *p):_p(p),use_count(1) { cout<<"U_ptr constructor called!"<<endl; } ~U_ptr() { if(use_count==0) delete _p; cout<<"U_ptr destructor has called!"<<endl; }};template<class T>class smartpointer{private: U_ptr<smartpointer,T>* _ptr;public: smartpointer(T* ptr):_ptr(new U_ptr<smartpointer,T>(ptr)) //构造函数 { cout<<"smartpointer constructor called!"<<endl; } T& operator *() //重载*运算符 { return *(_ptr->_p); } smartpointer& operator=(const smartpointer &sptr) //重载=运算符 { ++(sptr._ptr->use_count); //在减少左操作数的使用计数之前使rhs的使用计数加1,是为了防止自身赋值 if(--_ptr->use_count==0) delete _ptr; _ptr=sptr._ptr; return *this; } smartpointer(const smartpointer &sptr):_ptr(sptr._ptr) //拷贝构造函数 { ++_ptr->use_count; cout<<"smartpointer copy constructor called!"<<endl; } int use_count() //获取引用计数值 { return _ptr->use_count; } T* get_ptr() //获取指针地址 { return _ptr->_p; } ~smartpointer() //析构函数,计数为0时才释放指针指向的内存 { cout<<_ptr->_p<<" smartpointer deconstructor called!"<<endl; cout<<"before destruction use_count is="<<use_count()<<endl; if(--_ptr->use_count==0) delete _ptr; }};int test(){ smartpointer<int> p1(new int(8));//a should only be use to construct once cout<<"p1.use_count="<<p1.use_count()<<endl; cout<<"p1="<<p1.get_ptr()<<endl; smartpointer<int> p2(p1); cout<<"p1.use_count="<<p1.use_count()<<endl; cout<<"p2.use_count="<<p2.use_count()<<endl; cout<<"p1="<<p1.get_ptr()<<endl; cout<<"p2="<<p2.get_ptr()<<endl; smartpointer<int> p3(p1); cout<<"p1.use_count="<<p1.use_count()<<endl; cout<<"p3.use_count="<<p3.use_count()<<endl; cout<<"p1="<<p1.get_ptr()<<endl; cout<<"p3="<<p3.get_ptr()<<endl; smartpointer<int> p4(p3); cout<<"p3.use_count="<<p3.use_count()<<endl; cout<<"p4.use_count="<<p4.use_count()<<endl; cout<<"p3="<<p3.get_ptr()<<endl; cout<<"p4="<<p4.get_ptr()<<endl; p4 = p4; cout<<"p4.use_count="<<p4.use_count()<<endl; p4 = p3; cout<<"p4.use_count="<<p4.use_count()<<endl; cout<<"p3.use_count="<<p3.use_count()<<endl; cout<<"p4="<<p4.get_ptr()<<endl; cout<<"p3="<<p3.get_ptr()<<endl; return 0;}int main(){ test(); return 0;}
执行结果如下:
与实际应用STL中的智能指针结果相近
- 智能指针的理解
- 智能指针的理解
- 智能指针的理解
- 理解C++智能指针
- c++:智能指针
- C++:智能指针
- 【C++】智能指针
- 【C++】智能指针
- C++PJ智能指针
- 【c++】智能指针
- 【C++】智能指针auto_ptr
- C++,智能指针
- C++,boost智能指针
- 智能指针模拟C
- [C++]智能指针
- 【C++】 浅析智能指针
- C++_智能指针
- C++:初识智能指针
- lavarel错误和日志记录
- javascript 学习笔记(三):匿名函数与闭包实例
- Vue.js入门-内置指令v-html
- CodeForces 830B Cards Sorting(set乱搞)
- Mono.Cecil使用示例之给UnityEditor.dll中的ConsoleWindow添加双击委托
- 理解C++智能指针
- 不聊技术,对人对事的思考
- 小程序--滚动选择器picker篇
- 每天一道算法题——LeeCode.387. First Unique Character in a String
- SpringBoot DataSource配置详解
- 英语四六级考试忘记准考证?怎么办?
- docker 的简单使用
- Ubuntu 10.04设置终端窗口的默认大小
- C# 中TextReader中peek()与read()的异同