第15章 友元。异常和其他
15.1 友元
15.1.1 友元类
友元类的所有方法都可以访问原始类的私有成员和保护成员,也可以做更严格的限制,只将特定的成员函数指定为另一个类的友元。
friend class Remote; 使Remote成为友元类。
友元声明可以位于公有、私有或保护部分,其所在位置无关紧要。
15.1.2 友元成员函数
friend void Remote::set(TV & t , int c);
15.2 嵌套类
在另一个类中声明的类被称为嵌套类。该声明的类只在包含嵌套类声明的类中有效。
15.2.1 嵌套类和访问权限
嵌套类、结构和枚举的作用域特征
声明位置 | 包含它的类是否可以使用它 | 从包含它的类派生而来的类是否可以使用它 | 在外部是否可以使用 |
---|
私有部分 | 是 | 否 | 否 | 保护部分 | 是 | 是 | 否 | 公有部分 | 是 | 是 | 是,通过类限定符使用 |
15.3 异常
当cout输出一个被零除的数时,cout将这种值显示为Inf、inf、INF或类似的东西。
15.3.1 调用abort()
abort();函数位于cstdlib(stdlib.h)中。典型实现是向标准错误流发送消息abnormal program termination(程序终止),然后停止程序,他还返回一个随机实现而异的值。
也可以使用exit()函数,该函数刷新文件缓冲区,但不显示消息。
15.3.3异常机制
对异常的处理以下3部分:1.引发异常,2.使用处理程序处理异常,3.使用try块
throw语句实际上是跳转,throw关键字表示引发异常,紧接其后的值指出了异常的特征。导致程序沿函数调用序列后退,直到找到包含try块的函数。
catch关键字表示捕获异常,处理程序以catch开头。随后是位于括号中的类型声明,他指出了异常处理程序要响应的异常类型,然后是一个代码块,指出要采取的措施。
try块标识其中特定的异常可能被激活的代码块,他后面跟一个过多个catch块。
如果函数引发了异常,而没有try块或没有匹配的处理程序时,程序最终将调用abort()函数。
在catch中throw后无对象,则throw回catch的对象。
throw将向上进行栈解退直到遇到合适的catch对象。
15.3.4 异常规范和C++11
double harm(double a) throw(bad_thing);
double marm(double) throw();
其中的throw()部分就是异常规范,它可能出现在函数原型和函数定义中,可包含类型列表,也可不包含。
异常规范的作用之一是,告诉用户可能需要使用try块,让编译器检查。
可使用新增的关键字noexcept指出函数不会引发异常; double marm() noexcept;
15.3.7 其他异常特性
引发异常时编译器总是创建一个临时拷贝,即使异常规范和catch块中指定的是引用。
引发的异常对象将被第一个与之匹配的catch块捕获。
如不知道异常的类型,使用省略号来表示异常类型,从而捕获任何类型。
15.3.8 exception类
exception头文件(exception.h或except.h)定义了exception类。
C++可以把它用作其他异常类的基类。
有一个名为what()的虚拟成员函数,它返回一个字符串,他是一个虚方法。
头文件stdexception定义了logic_eror和runtime_error类,他们都是以公有方式从exception派生而来的。
这些类的构造函数接受一个string对象作为参数,该参数提供了方法what()以C风格字符串方式返回的字符数据。
logic描述了典型的逻辑错误,子类有domain_error,invalid_argument,length_error,out_of_bounds。
domain_error指出函数在参数不是定义域之间。 invalid_argument指出给函数传递了一个意外的值。 length_error用于指出没有足够的空间来执行所需的操作。 out_of_bounds通常用于指示索引错误。
runtime_error异常描述了可能在运行期间发生但难以预计和防范的错,子类有range_error,overflow_error,underflow_error。
在new导致的内存问题发生时,C++会让new引发bad_alloc异常,头文件new包含bad_alloc类的声明,他是从exception类公有继承而来的。
C++标准提供了一种在失败时返回空指针的new。 int* pi = new (std::nothrow) int;
15.3.10 异常何时会迷失方向
如果它是在带异常规范的函数中引发的,则必须与规范列表中的某种异常匹配,否则称为意外异常。程序将调用unexpected()函数,这个函数调用terminate(),后者在默认情况下调用abort()。
使用set_unexpected()函数修改unexpected()的行为。
如果异常不是在函数中引发的(或者函数没有异常规范),则必须捕获它。如果没被捕获(在没有try块或没有匹配的catch块时,将出现这种情况),则异常被称为未捕获异常。未捕获异常不会导致程序立刻异常终止,而是首先调用函数terminate(),在默认情况下terminate()调用abort(),这将导致程序异常终止。。
可以指定terminate()函数应调用的函数来修改terminate()的这种行为。
set_unexpected()、set_terminate()和terminate()都是在头文件exception中声明的。
set_unexpected()、set_terminate()函数将不带任何参数且返回类型为void的函数名称地址作为参数。并返回上一次设置的函数的地址。
unexpected_handler()函数可以:
- 通过terminate()(默认行为)、abort()或exit()来终止程序。
- 引发异常。
- 如果新引发的异常与原来的异常规范匹配,则程序将从哪里开始进行正常处理,
- 如果新引发的异常与原来的异常规范不匹配,且异常规范中没有包括std::bad_exception类型,则程序将调用terminate(),bad_exception是从exception继承过来的,其声明位于头文件exception中。
- 如果新引发的异常于原来的异常规范不匹配,且原来的异常规范中包含了std::bad_exception类型,则不匹配的异常被std::bad_exception取代。
15.3.11 有关异常的注意事项
异常规范不适用于模板
5.4 RTTI
dynamic_cast回答是否可以安全地将对象的地址赋给特定类型的指针。
Superb *pm = dynamic_cast<Superb *>(pg);
如果不能安全转换,则返回空指针。
对于引用,当请求不正确时,dynamic_cast将引发类型为bad_cast的异常,从exception派生为来的,在头文件typeinfo中。
typeid运算符接受类名或者结果为对象的表达式,返回一个type_info对象的引用,其中,type_info定义在头文件typeinfo(typeinfo.h)
type_info重载了==和=运算符,以便可以使用这些运算符来对类型进行比较
type_info包含一个name成员,该函数返回一个随实现而异的字符串。
如果传入typeid的是一个空指针,程序将引发bad_typeid异常,从exception继承,定义在头文件typeinfo。 type_info提供了一个name()的函数,通常显示类名。
15.5 类型转换运算符
强制转换运算符:dynamic_cast、const_cast、static_cast、reinteropret。
const_cast运算符用于改变值为const或volatile。
static_cast运算符仅当type_name可被隐式转换为expression所属的类型或expression可被隐式转换为type_name所属的类型时才合法。
reinterpret_cast运算符用于天生危险的类型转换,其语法与其他强制类型转换符相同。 通常这样的转换适用于用于依靠于实现的底层编程技术,是不可以移植的。 不能将函数指针转换为数据指针、不能将指针转换为更小的整型或浮点型。
|