引入智能指针:
智能指针的实现原理:
资源分配即初始化RAII(Resource Acquisition Is Initialization):
定义一个类来封装资源的分配和释放,在构造函数完成资源的分配
和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放。
实现机制:利用类的构造和析构函数(释放资源)是由编译器自动调用的。
智能指针:
1.管理指针执行对象的释放问题。
2.可以像指针一样使用。
在c++标准库里主要有四个智能指针:
C++四种智能指针auto_ptr、unique_ptr、shared_ptr和weak_ptr。
其中auto_ptr是C++98标准化引入的;
scope_ptr、shared_ptr和weak_ptr是C++11标准化才引入的(当然,早在C++03的TR1中就已经可以使用了)。我们都知道,auto_ptr虽说简单,但使用起来却到处是坑,以至于大家都不提倡使用。
shared_ptr是引用计数的智能指针,被奉为裸指针的完美替身,因此被广泛使用。也可能正是这个原因,scope_ptr 和 weak_ptr似乎被大家遗忘了(或许还会以为它们纯属多余)。
上述言论引自网上
下面简单总结下这几个智能指针:
下面简单总结下这几个智能指针:
1.auto_ptr管理权转移
带有缺陷的设计 ----->c++98/03
在任何情况下都不要使用;
在任何情况下都不要使用;
2.scoped_ptr(boost)
unique_ptr(c++11)
防拷贝--->简单粗暴设计--->功能不全。
3、shared_ptr(boost/c++11)
引用计数--->功能强大(支持拷贝、支持定制删除器)
缺陷---->循环引用(weak_ptr配合解决)。
这篇文章主要的内容是讲解auto_ptr所引出的一系列问题:
一、auto_ptr智能指针
(无论在什么情况下都不要使用,下面来详细介绍下为什么不要使用它,可能会有人问,既然不让使用,那么为什么还要把它放在c++的标准库里呢?
是的,一开始我也是有这个疑问的,--->其实是因为c++标准库里的内容一经规定,就不允许修改了)
下面模拟实现其基本功能:
1.AutoPtr的第一种实现方法:
采用资源转移的方法。
一个指针将一块空间的权限完全交给了另一个指针。(权限的转移)
模拟实现代码如下:
赋值运算符存在问题:
(由此可知对于动态内存空间只能由一个智能指针来管理,从而避免下面的问题。
存在的问题:
问题①:这种将一块空间的权限完全交给别人的方法:
产生问题的是拷贝构造和赋值运算符重载函数。
当调用了拷贝构造或是赋值运算符的重载函数任意一个时:
假设调用了拷贝构造函数,我们的本意是用一个对象去构造一个与之一模一样的对象,可是结果呢,我们把自己给弄丢了,完全没有达到我们预期的目标。
所以当我们在上述代码中的Test1中调用了拷贝构造函数时,如果我们想要修改第一次构造出来的对象的内容时,会发现对象的资源已经被清理了,所以会导致程序崩溃。
问题②:先看如下示例:
问题③:
AutoPtr<int> ap(AutoPtr<int>(new int(1)))
//括号里是构造一个无名对象,无名对象,具有常性,和第②个问题一样,在拷贝构造ap时,拷贝构造函数的参数应该由const来修饰,还有一个问题就是,在vs2010下,编译器对其做了一些优化,本来这一条语句,应该是先调用构造函数构造匿名对象,然后由匿名对象去拷贝构造对象ap,但是由于在构造了匿名对象之后,又马上去拷贝构造对象,而且是在一条语句执行的,所以编译器对其进行了优化,只会调用构造函数,而不会去调用拷贝构造函数,将构造的无名对象值直接给了ap这个对象,所以在vs2010下程序并没有崩溃,而在Linux下编译时就会报出错误。
下面是我在Linux下测试时的代码:
编译错误:
总结:由上面的编译错误我们可以得知,这种由一个常性对象去拷贝构造一个Auto_ptr的想法是不现实的,所以才有了第三种情况。
实际上,实现方法三就是为了解决用一个临时右值对象去拷贝构造一个对象。
2.AutoPtr的第二种实现方法:
存在野指针的问题:
同样用示例来分析这个问题:
会发生内存泄漏,因为ap4与ap5共同管理一块空间,当ap5出了if语句的作用域后,已被析构,空间被回收,那么在下面在去访问ap4就有了问题,ap4已然成为了一个野指针。
③类型转化
c++标准库里的的auto_ptr的实现:
但还有是有缺陷:访问空指针的问题无法规避,也就是实现方法一存在的第一个问题
我先贴出标准库里的源码:
示例分析:
如果您还想知道标准库里究竟是怎样实现的,可以继续往下看,希望可以对您有所帮助。
源码分析:
为了模拟一个一般指针,所以重载以下两个符号是为了使得智能指针可以像一般指针一样使用。
智能指针内封装的一个原生态指针:
二、ScopedPtr(独占空间--->防拷贝、防赋值)
(其实在c++标准库里称为unique_ptr,很好理解了,因为AutoPtr就是因为转移资源,以及转交权限从而引发一系列的问题的,追根到底其实就是拷贝构造和赋值运算符重载函数导致的。所以这个智能指针就是防拷贝和赋值的)。
因为AutoPtr智能指针在拷贝构造和赋值运算符重载方面有些许问题,所以就有了ScopedPtr,既然拷贝构造和赋值运算符重载会引发一些问题,那么是不是可以不允许它拷贝和赋值呢?
既然拷贝和赋值容易出现问题,那么这种智能指针就不允许拷贝和赋值。
解决上述问题的方法:
实现这种机制的方法有三种:
1)将拷贝构造函数和赋值运算符重载函数的声明写成公有的,但是对这两个函数不进行定义。
2)将拷贝构造函数和赋值运算符重载函数在类型直接实现,但是将它们的访问限定符设定为私有的。
3)将拷贝构造函数和赋值运算符重载函数的声明写成私有的,但是对这两个函数不进行定义。
分析上述三种方法:
第一种:如果将其声明为公有的,那么如果有其他人在类外对你的拷贝构造和赋值运算符重载函数进行实现,那么还是没有达到防拷贝、防赋值的要求。--->pass掉
第一种:如果将其声明为公有的,那么如果有其他人在类外对你的拷贝构造和赋值运算符重载函数进行实现,那么还是没有达到防拷贝、防赋值的要求。--->pass掉
第二种:个人觉得,既然都已经不允许拷贝和赋值了,为什么还要多此一举的写出其实现方法呢?所以呢----->pass掉
所以:就第三种方法觉得合适。
在这里我只贴出第三种方法的代码:
scoped_array//管理数组
(boost库里面的,c++标准库由于已经有相似功能的vector,所以并未将其此加入)
总结:c++标准库里的智能指针只能管理单个对象的动态空间,而不能让其管理一段连续空间,例如:动态开辟的数组的空间。
总结:c++标准库里的智能指针只能管理单个对象的动态空间,而不能让其管理一段连续空间,例如:动态开辟的数组的空间。
auto_ptr无论什么时候都不要使用
scoped_ptr是对auto_ptr所存在的问题的一个解决。
所以一般面试时,没有特别指定让你实现哪个智能指针,最好写出这个,因为这个最简单呀,千万不要写出auto_ptr,这个智能指针存在缺陷!
虽然c++标准库里面加入了auto_ptr智能指针,但是还是不要使用它,c++标准库里仍然保留它,应该是为了向前兼容。
因为既然已经加入了标准库,那么肯定会有程序员使用的,所以还是不能随意废弃掉。
后续我还会继续总结关于shared_ptr的实现细节。
题外话:
因为刚开始接触智能指针,自身能力不足,所以哪里写的有问题的欢迎大家提出来。
共同进步!
每天进步一点点!