【M28】智能指针
1、什么是智能指针?
所谓智能指针就是,看起来,用起来,感觉起来都像原始指针,但是提供了更多功能。
2、使用智能指针取代原始指针,可以获得更多的控制权。如下:
a、在构造和析构的时候,可以做一些事。
b、在复制和赋值的时候,可以做一些事。
c、在解引用的时候,可以做一些事。
3、智能指针可以对不同类型的指针进行封装,因此智能指针是一个模板类。
4、智能指针对原始指针封装,内含一个原始指针,为了用起来像指针,重载->和*,->返回原始指针,*返回对象的引用,两个重载操作符都不改变智能指针,因此是const成员方法。
5、考虑auto_ptr,拥有权转移,auto_ptr的copy构造会修改rhs,因此rhs不是const T&,而是T&。C++默认是传值,导致拥有权转移,如果期望拥有权不转移,使用传引用。
6、解引用操作符*,必须返回引用,不能返回对象。因为解引用操作符,可能返回T的子类。如果返回对象,首先导致对象复制,其次可能照成对象切割,失去多态功能。
7、判断智能指针是否为NULL。对于原始指针,使用if(pa == null),但是对于智能指针,不能这样使用,因为智能指针是个对象。怎么解决这个问题呢?
a、增加isNull方法,缺点是与原始指针的用法不一致。
b、为了与原始指针的用法一致,增加隐式类型转换操作符,operator void*()。但是这会导致一个问题,任意两个智能指针,都能进行比较,因为他们都可以隐式转化为void*,这不是我们所期望的。
8、智能指针如何暴露出原始指针?
使用&*pa,合法,先取引用,再取地址。但是这种写法太难看了。增加隐式类型转换操作符operator T*(),这会引入一个问题,不是我们期望的转换,编译器也会偷偷进行。使用get方法,返回原始指针,避免编译器偷偷地进行转换。
注意:暴露原始指针是危险的,因为暴露出去的原始指针,不再受控。比如:智能指针把资源delete了,外面的原始指针就是野指针了。或者外面的指针执行了delete,智能指针管理的指针指向垃圾。
9、考虑继承,C++是强制类型的。Smart_Ptr<Base>与Smart_Ptr<Derived>是两个完全不同的类,之间没有任何关系,更谈不上继承。那么问题来了,对于原始指针可以指向子类对象,而智能指针不行。如下:
Base* b = new Derived(); // OK
Smart_Ptr<Base> b = Smart_Ptr<Derived>(new Derived); //Error
编译失败,这不合理,怎么解决这个问题呢?
10、首先想到的是,隐式类型转换。这显然不合理,对原始指针类型的所有转换都要考虑。
11、有没有更好的解决办法呢?
使用C++语言的新特性,成员方法模版。隐式类型转换操作符声明为成员方法模版,如下:
template <typename NewType>
operator Smart_Ptr<NewType>()
{
return Smart_Ptr<NewType>(this->ptr);
}
考虑Smart_Ptr<Base> b = Smart_Ptr<Derived>(new Derived)的执行过程:
a、编译器首先看看Smart_Ptr<Base>有没有一个构造方法,该构造方法只需要一个形参,形参类型为Smart_Ptr<Derived>,没有找到;
b、编译器看看Smart_Ptr<Derived>有没有隐式类型转换操作符,将Smart_Ptr<Derived>转换为Smart_Ptr<Base>,没有找到;
c、编译器尝试将成员方法模版实例化出来一个合适的方法,也就是一个隐式类型转换操作符,使得隐式类型转换成功。
将NewType实例化为模版实参Base,在return语句的构造方法中,使用原始指针Derived,初始化Base指针,构造出Smart_Ptr<Base>,当然可以。
注意,这里还放大了权限,只要NewType指针可以转换为T指针,那么Smart_Ptr<NewType>对象就可以赋值给Smart_Ptr<T>对象。
成员方法模版,还有两个问题。首先,考虑fun(Base&),fun(Derived&),对于Derived derived1, fun(derived1)的调用,fun(Derived&)的匹配程度更高。但是,对于成员方法模版,都可实例出一个方法,使得调用成功,并且匹配程度是一样的,这就导致不确定的调用,编译错误。其次,成员方法模版移植性不高,有些编译器不支持。
12、智能指针与const
C++设计理念是,最小特权原则。让别人做一件事,尽量限制他的权利。一个典型的例子就是,C++在传递引用过程中,可以缩小权力,但不能放大权利。
原始指针与const有两种关系:a、指向const对象的指针,不能修改指向的对象;b、指针常量,不能修改指向。因此,对于智能指针,我们也希望有这种弹性。但是,对于智能指针只有一个地方可以const,修饰智能指针对象,也就相当于修饰指针。那怎么样修饰指向的对象呢?使用Smart_Ptr<const T>。那么问题来了,Smart_Ptr<T> 与Smart_Ptr<const T>是两个完全不同的类型,当然不能赋值。该怎么办呢?不要再想着隐式类型转换了,因为隐式类型转换会导致非预期的转换。考虑里氏代换,子类能力更大。从const T 到T,是能力变大的过程,因此可以考虑Smart_Ptr<T>: Smart_Ptr<const T>
【M28】智能指针的更多相关文章
- C++ 智能指针auto_ptr
template<class T> class auto_ptr { public: ); // Item M5 有“explicitfor”// 的描述 template<clas ...
- enote笔记法使用范例(2)——指针(1)智能指针
要知道什么是智能指针,首先了解什么称为 “资源分配即初始化” what RAII:RAII—Resource Acquisition Is Initialization,即“资源分配即初始化” 在&l ...
- C++11 shared_ptr 智能指针 的使用,避免内存泄露
多线程程序经常会遇到在某个线程A创建了一个对象,这个对象需要在线程B使用, 在没有shared_ptr时,因为线程A,B结束时间不确定,即在A或B线程先释放这个对象都有可能造成另一个线程崩溃, 所以为 ...
- C++智能指针
引用计数技术及智能指针的简单实现 基础对象类 class Point { public: Point(int xVal = 0, int yVal = 0) : x(xVal), y(yVal) { ...
- EC笔记:第三部分:17、使用独立的语句将newed对象放入智能指针
一般的智能指针都是通过一个普通指针来初始化,所以很容易写出以下的代码: #include <iostream> using namespace std; int func1(){ //返回 ...
- 智能指针shared_ptr的用法
为了解决C++内存泄漏的问题,C++11引入了智能指针(Smart Pointer). 智能指针的原理是,接受一个申请好的内存地址,构造一个保存在栈上的智能指针对象,当程序退出栈的作用域范围后,由于栈 ...
- 智能指针unique_ptr的用法
unique_ptr是独占型的智能指针,它不允许其他的智能指针共享其内部的指针,不允许通过赋值将一个unique_ptr赋值给另一个unique_ptr,如下面错误用法: std::unique_pt ...
- 基于C/S架构的3D对战网络游戏C++框架_05搭建系统开发环境与Boost智能指针、内存池初步了解
本系列博客主要是以对战游戏为背景介绍3D对战网络游戏常用的开发技术以及C++高级编程技巧,有了这些知识,就可以开发出中小型游戏项目或3D工业仿真项目. 笔者将分为以下三个部分向大家介绍(每日更新): ...
- C++ 引用计数技术及智能指针的简单实现
一直以来都对智能指针一知半解,看C++Primer中也讲的不够清晰明白(大概是我功力不够吧).最近花了点时间认真看了智能指针,特地来写这篇文章. 1.智能指针是什么 简单来说,智能指针是一个类,它对普 ...
随机推荐
- JVM 性能调优实战之:一次系统性能瓶颈的寻找过程
玩过性能优化的朋友都清楚,性能优化的关键并不在于怎么进行优化,而在于怎么找到当前系统的性能瓶颈.性能优化分为好几个层次,比如系统层次.算法层次.代码层次…JVM 的性能优化被认为是底层优化,门槛较高, ...
- 怎样为EXCEL2010添加下拉列表
注意,下面是Excel2010的步骤和截图,其他版本的Excel类似. 首先用鼠标左键点击你要添加下拉列表的单元格. 如果你只想部分区域有下拉列表,也可以选择部分区域. 下面图片是选择的整个列都是 ...
- IOS AutoLayout 遍历修改约束
self.cvv2View.hidden = YES; self.periodView.hidden = YES; [self.contentView.constraints enumerateObj ...
- Introduction 引言
The risk of software failure has never been greater. The estimated annual economic impact ranges fro ...
- 【Unity入门】编辑器常用视图介绍
版权声明:本文为博主原创文章,转载请注明出处. 打开Unity编辑器的主窗口,在窗口的右上角可以看到有个“Layout”按钮.这是用来对Unity编辑器主窗口上面的各个窗口面板进行布局的.通常情况下我 ...
- 谈谈final、finally和finalize
final: final为修饰符, 如果类被声明为final,则不能派生新子类. 如果变量被声明为final,则必须在声明时初始化,在以后的引用只能读取,不可修改. 如果方法被final声明,则只能使 ...
- oracle 数据库远程导出
exp 用户名/密码@IP:端口/数据库名 file=文件路径 full=y; exp scebm1/ebm@10.3.10.16:1521/scebm file=D:scebm20140527.dm ...
- Ubuntu上用快捷键关闭没有响应的程序
Linux 上有很多方法可以强制关闭无响应的程序,比如你可以通过按快捷键 Ctrl + Shift + T 来调出 Terminal 或者用 Ctrl + Shift + F1 进入 Console ...
- Html.DropDownListFor
@Html.DropDownListFor(x => x.WillAttend, new[] { new SelectListItem() {Text = "Yes, I'll be ...
- Spark RDD概念学习系列之RDD的操作(七)
RDD的操作 RDD支持两种操作:转换和动作. 1)转换,即从现有的数据集创建一个新的数据集. 2)动作,即在数据集上进行计算后,返回一个值给Driver程序. 例如,map就是一种转换,它将数据集每 ...