C++Prime Plus(5)

85.异常(1)异常处理机制

异常:运行错误(比如无法打开文件,动态内存申请失败),导致程序无法继续正常运行;

处理异常的常用方法:

1.调用stdlib中的abort()函数:输出一个程序异常终止的信息,然后终止程序;
fig1
void abort(void) 用于中止程序执行,直接从调用的地方跳出;

2.利用函数的返回值报告错误;
fig2
其中,DBL_MAX是double型的最大值。

示例:
fig3
异常机制
遇到异常时,将控制权从程序的一个部分转移到另一个部分;

异常处理的组成部分
try块:用于标识可能抛出异常的程序块;
throw抛出异常:程序检测到异常时,跳出当前部分;
catch捕获异常:处理异常的程序;

示例:
fig4
将对象用作异常类型
throw可以抛出任何类型的值,catch捕获抛出值的类型(我们可以用不同的类型标识不同的错误)
我们可以设计一个简单的异常类型:
fig5
用异常类型区分不同的错误
如果程序中用到多个会抛出异常的函数,可以通过抛出的类型确定不同的处理过程:
fig6
fig7

86.异常(2)exception类

exception类
对于上一节中,用异常类型区分不同的错误,需要重复写catch,现在我们可以使用C++提供的标准类exception类,用作其他异常类的基类,其中有一个返回字符串的虚函数what(),用于返回出错的原因(利用了C++的多态性)

fig8
现在对于不同的异常类型,只需要一个catch就能调用不同的成员(利用了多态性),取决于try内部的操作抛出哪个异常类,但在catch中都是用基类去指向派生类。
fig9
bad_alloc异常类
对于异常类,除了exception基类,C++还有一个bad_alloc异常类:

  • 申请动态变量失败时,早期的C++返回一个空指针,现在的C++则是抛出bad_alloc异常类,bad_alloc从exception派生,提供了what()
    fig10

87.RTTI(1)

RTTI:运行阶段类型识别(Runtime Type Identification)

RTTI用途:
1.跟踪对象类型;
2.在继承结构中确定类型转换的安全性;

支持RTTI的元素
1.typeid运算符返回一个type_info类类型的值,type_info存储了有关特定类型的信息;
2.用dynamic_cast检查继承层次中基类指针或引用向派生类转换的合法性;

typeid运算符和type_info类
运算符typeid:获取对象的类型信息,是一元运算符
运算对象:类名 或 结果为对象的表达式
返回值:type_info对象的引用

type_info类:有一个成员函数name(),返回类或者对象对应的类名

示例
fig11

88.RTTI(2)

dynamic_cast运算符:用于检查是否可以安全将基类地址转换成派生类地址。
fig12
直接进行类型强制转换,编译器不会检查,会造成后期的内存读写错误,应该使用RTTI,如果返回空指针,对程序的后续行为执行才是安全的。

89.类型转换运算符

C语言风格的强制类型转换:(类型名) 表达式

比如:

int a=5;
char* p=(char*) &a;
int* pi=(int*) p;

C风格的强制类型转换缺点:没有检查或指明所做转换的意义。

C++严格规范转换限制,使得转换过程更加规范,并设置了4个类型转换运算符:

  • dynamic_cast:用于检查是否可以安全将基类地址转换成派生类地址。
  • const_cast
  • static_cast
  • reinterpret_cast

const_cast:删除对象的常量性质
fig13
输出结果为:
fig14
可以发现,其实const修饰的常量并没有被修改(因为常量在内存的不可变区域),const_cast的用途其实是:

  • 当调用了一个形式参数不是const的函数,但实际参数是const的,但是我们确定这个函数不会对参数做修改,因此需要const_cast去除实际参数的const限定,以便函数能够接受这个实际参数。

static_cast:编译器支持的隐式转换(自动类型转换)
比如:

double x=1.998;
int a;
a=static_cast<int> x;

可以看出,static_cast是安全的转换,只是会丢失精度;


C语言自动类型转换出现的场合
1.赋值或初始化时,如果右边的表达式计算结果类型与左边的变量类型不同:右边值被转换成左边变量类型;
2.函数参数传递时,实际参数被转换成形式参数的类型;
3.表达式中的运算数类型不一致,需要遵循转换规则:把精度小的运算数向精度大的运算数转换;


reinterpret_cast:危险的转换,重新解释地址中的内容
比如:
fig15
reinterpret_cast这种转换看起来是没有意义的(因为两个变量之间没有关联关系),这种转换更多用在输入输出场景(把内存中的一块空间内容完整写入一个文件中)

C++相比C,增加了类型转换运算符,一是为了让编译器检查类型转换的合理性,二是为了让读代码的程序员看到当前类型转换的意义。

90.string类

对于C风格字符串:
用字符数组存储
无法用运算符进行操作
执行字符串操作时需要特别注意空间问题

C++中专门处理字符串的类,在库string中,用动态数组存储(动态数组可以回顾20.数组的替代品中的vector,注意到另一个替代品array类可以回顾78.类模板(2)非类型参数),必要时会自动扩大空间
功能:

  • 不同的构造方法
  • 字符串赋值(=)
  • 字符串连接(+)
  • 字符串比较(>, <, >=, <=, ==, !=)
  • 字符串输入输出(>>, <<)
  • 用下标访问字符串的元素

示例:
fig16

91.智能指针

问题引入
fig17
上述函数存在内存泄漏的可能:当抛出异常时,局部变量ps的空间被释放,但ps指向的动态变量没有释放。

解决方案是使用智能指针;

智能指针:可以在释放指针本身的空间时候,调用它指向的对象的析构函数,从而析构该指针指向的对象

智能指针是一个类模板,模板参数是智能指针指向的对象类型。智能指针包含在库memory中。常用的智能指针有3种:

  • auto_ptr:C++98提出
  • unique_ptr:C++11提出
  • shared_ptr:C++11提出

智能指针示例
fig18
3种智能指针的区别:多个指针指向同一个对象时,3种指针有不同的行为

auto_ptr:在赋值时,会将右边指针的控制权转移给左边指针,右边的指针就失去了控制权(自动指向NULL),这种情况下,对右边指针进行读写操作会出错;

unique_ptr:明确指出不允许共享,当发生指针赋值时,编译出错;

shared_ptr:允许多个指针指向同一个对象,析构时,只有最后一个析构的指针会调用对象的析构函数;

auto_ptr示例
fig19
unique_ptr示例
fig20
shared_ptr示例
fig21


注意到85节中的抛出异常类throw bad_hmean(a, b);语句,bad_hmean是一个类类型;

91节智能指针中,出现auto_ptr<string> ps (new string("sentence"))auto_ptr<string> (new string("sentence"))这种语句;

这些类的实例化方式,可以回顾53.对象构造中的内容

  • throw bad_hmean(a, b);中的bad_hmean(a, b)是构造函数的显式调用
  • auto_ptr<string> ps (new string("sentence"))是构造函数的隐式调用(定义对象时自动调用);
  • auto_ptr<string> (new string("sentence"))是构造函数的显式调用

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值