===19.1.1 dynamic_cast 操作符===
dynamic_cast 操作符可以用来把一个类类型对象的指针转换成同一类层次结构中的其他类的指针
同时,也可以用它把一个类类型对象的左值转换成同一类层次结构中其他类的引用
如果针对指针类型的dynamic_cast失败则dynamic_cast 的结果是0
void company::payroll( employee *pe )
{
// dynamic_cast 和测试在同一条件表达式中
if ( programmer *pm = dynamic_cast< programmer* >( pe ) ) {
// 使用 pm 调用 programmer::bonus()
}
else {
// 使用 employee 的成员函数
}
}
如果针对引用类型的dynamic_cast 失败则dynamic_cast会抛出一个异常
void company::payroll( employee &re )
{
try {
programmer &rm = dynamic_cast< programmer & >( re );
// 用 rm 调用 programmer::bonus()
}
catch ( std::bad_cast ) {
// 使用 employee 的成员函数
}
}
dynamic_cast 被用来执行从基类指针到派生类指针的安全转换,它常常被称为安全的向下转换downcasting
===19.1.2 typeid 操作符===
用于获取一个表达式的类型
1、typeid 操作符必须与表达式或类型名一起使用
2、内置类型的表达式和常量可以被用作typeid 的操作数,当操作数不是类类型时,typeid 操作符会指出操作数的类型
int iobj;
cout << typeid( iobj ).name() << endl; // 打印: int
cout << typeid( 8.16 ).name() << endl; // 打印: double
3、当typeid 操作符的操作数是类类型,但不是带有虚拟函数的类类型时,typeid 操作符会指出操作数的类型而不是底层对象的类型
class Base { /* 没有虚拟函数 */ };
class Derived : public Base { /* 没有虚拟函数 */ };
Derived dobj;
Base *pb = &dobj;
cout << typeid( *pb ).name() << endl; // 打印: Base,如果Base有虚拟函数则打印Drived
4、
#include <type_info>
employee *pe = new manager;//employee含虚拟函数
employee& re = *pe;
if ( typeid( pe ) == typeid( employee* ) ) // true,pe为指向employee的指针
// do something
typeid( *pe ) == typeid( manager ) // true pe的底层对象类型为manager
typeid( *pe ) == typeid( employee ) // false
===19.1.3 type_info 类===
typeid 操作符实际上返回一个类型为type_info 的类对象,type_info 类类型被定义在头文件<type_info>中
===19.2.2 抛出类类型的异常===
void iStack::push( int value )
{
if ( full() )
// value 被存储在异常对象中.
throw pushOnFull( value );
// ...
}
执行该throw 表达式会发生许多个步骤
1 throw 表达式通过调用类类型pushOnFull 的构造函数,创建一个该类的临时对象
2 创建一个pushOnFull 类型的异常对象,并传递给异常处理代码,该异常对象是第1步throw 表达式创建的临时对象的拷贝,它通过调用pushOnFull 类的拷贝构造函数而创建
3 在开始查找异常处理代码之前,在第1 步中由throw 表达式创建的临时对象被销毁
===19.2.3 处理类类型的异常===
一旦编译器为一个异常按顺序找到了一个catch 子句,就不会再检查进一步的catch 子句
对于一个异常对象,直到该异常的最后一个catch 子句退出时它才被销毁
===19.2.5 栈展开和析构函数调用===
class PTR {
public:
PTR() { ptr = new int[ chunk ]; }
~PTR() { delete[] ptr; }
private:
int *ptr;
};
void manip( int parm ) {
PTR localPtr;
// ...
mathFunc( parm ); // 抛出 divideByZero 异常
// ...
}
如果mathFunc()抛出一个divideByZero 类型的异常,则栈展开过程在函数调用链中向
查找该异常的catch 子句。在栈展开过程中函数manip()会被检查到,因为函数mathFunc()
调用没有被放在try 块中,所以不会在manip()中查找针对该异常的catch 子句,栈展开过程
继续向上遍历函数调用链,到达调用函数manip()的函数即上一个调用函数,然而在
manip()带着这个未处理的异常退出之前,栈展开过程会销毁manip()中所有在调用函数
mathFunc()之前被创建的局部类对象,栈展开过程在函数调用链中继续向上前进,在前进
之前局部对象localPtr 先被销毁由localPtr 指向的空闲存贮区也被释放以防止内存泄漏
===19.2.6 异常规范===
1、如果一个成员函数被声明为const 或volatile 成员函数,则异常规范跟在函数声明的const 和volatile 限定修饰符之后
2、基类中虚拟函数的异常规范可以与派生类改写的成员函数的异常规范不同,但是派
生类虚拟函数的异常规范必须与基类虚拟函数的异常规范一样或者更严格
3、在被抛出的异常的类型与异常规范中指定的类型之间不允许进行类型转换,
这个规则有一个小小的例外,当异常规范指定一个类类型或类类型的指针时,
如果一个异常规范指定了一个类,则该函数可以抛出从该类公有派生的类类型的异常对象
===19.2.7 构造函数和函数try 块===
inline Account::Account( const char* name, double opening_bal )
try //关键字try 被放在成员初始化表之前
: _balance( opening_bal - serviceCharge() )
{
_name = new char[ strlen(name)+1 ];
strcpy( _name, name );
_acct_nmbr = get_unique_acct_nmbr();
}
catch ( ... )
{
// 特殊处理
// 现在能够捕获来自 ServiceCharge() 的异常了
}
===19.3 重载解析过程和继承===
1 选择候选函数
2 选择可行函数
3 选择最佳匹配函数
如果普通函数调用的实参是类类型的对象、类类型的指针或类类型的引用,则候选函数是以下集合的并集
1 在调用点上可见的函数
2 在定义该类类型的名字空间或定义该类的基类的名字空间中声明的函数
3 该类或其基类的友元函数
===19.3.3 最佳可行函数===
a、在为实参上的转换划分等级时,下列这些转换具有标准转换的等级
1 把派生类类型的实参转换成任何一个基类类型的参数
2 把派生类类型的指针转换成任何一个基类类型的指针
3 用派生类类型的左值初始化基类类型的一个引用
注意:标准转换序列好于用户定义的转换序列,即标准转换等级高于用户定义转换
b、对于从派生类类型到不同基类类型的不同标准转换进行等级划分时,对于从派生类类型
到基类类型移动较少(距离较近)的转换被认为好于移动较多(距离较远)的转换
c、到基类类型指针的标准转换好于到void*的转换
用一个基类类型的对象初始化一个派生类对象,或初始化一个派生类类型的引用,或者
从一个基类类型的指针到派生类类型的指针的转换都不会被当作一个隐式转换来应用
extern void release( const Bear& );
extern void release( const Panda& );
ZooAnimal za;
// 错误: 没有匹配
release( za );