15.1 友元
- 友元类:友元类的所有方法都可以访问原始类的私有成员和保护成员,比如模拟电视机和遥控器之间的关系,需要使用友元的另一种情况是,函数需要访问两个类的私有数据,友元声明可以位于公有,私有或保护部分,其所在的位置无关紧要 friend class Remote
- 友元成员函数声明
class Tv;
class Remote{…};
class Tv{…}; //put Remote method definitions here
15.2 嵌套类
在C++中,可以将类声明放在另一个类中,在另一个类中声明的类被称为嵌套类,对类进行嵌套通常是为了帮助实现另一个类,并避免名称冲突
- 如果嵌套类是在另一个类的私有部分声明的,则只有后者直到它
- 如果嵌套类是在另一个类的保护部分声明的,则它对于后者来说是可见的
- 如果嵌套类是在另一个类的公有部分声明的,则允许后者,后者的派生类以及外部世界使用它,类可见后,访问控制规则将决定程序对嵌套类成员的访问权限
15.3 异常
- 对于异常,处理方式之一是,如果其中一个参数是另一个参数的负值,则调用abort()函数,abort()函数的原型位于头文件cstdib中,其典型实现是向标准错误流发送消息abnormal program termination,然后终止程序,它还返回一个随实现而异的值,告诉操作系统,处理失败。abort()是否刷新文件缓存区取决于实现。如果愿意,也可以使用exit(),该函数刷新文件缓存区,但不显示消息。调用abort()将直接终止程序,而不是先返回到main()
- 一种比异常终止更灵活的方法是,使用函数的返回值来指出问题
- C++异常是对程序运行过程中发生的异常情况的一种相应,异常提供了将控制权从一个部分传递到另一个部分的途径,对异常的处理有三个组成部分
- 引发异常
- 使用处理程序捕获异常
- 使用try块
try { //start of try block
z = hmean(x,y);
} //end of try block
catch (const char * s)
{
std::cout<< s << std::endl;
}
double hmean(double a, double b)
{
if (a == -b)
throw "bar param";
}
- 通常,引发异常的函数将传递一个对象。这样做的优点是,可以使用不同的异常类型来区分不同函数在不同情况下引发的异常。另外,对象可以携带信息,程序员根据这些信息来确认引发异常的原因
异常规范:C++ 98新增的一项功能,但C++ 11摈弃了 double harm(double a) throw(bad thing)
-异常规范作用之一是,告诉用户可能需要使用try块,然而,这项工作也可以使用注释轻松完成,异常规范的另一个作用是,让编译器添加指向运行阶段检查的代码,检查是否违反了异常规范,着很难检查
,C++ 11使用新增关键字noexcept指出函数不会引发异常,理由是知道函数不会引发异常有助于编译器优化代码
栈解退:函数由于出现异常而终止,则程序也将释放栈中的内存,但不会在释放栈的第一个返回地址后停止,而是继续释放栈,直到找到一个位于try块中的返回地址,随后,控制权将转到块尾的异常处理程序,而不是函数调用后面的第一条语句。
- 引发机制重要特性:和函数返回一样,对于栈中的自动类对象,类的析构函数将被调用,然而,函数返回仅仅处理该函数放在栈中的对象,而throw语句则处理try块和throw之间整个函数调用序列放在栈中的对象,如果没有栈解退这种特性,则引发异常后,对于中间函数调用放在栈中的自动类对象,其析构函数不会被调用
- 虽然throw-catch机制类似于函数参数和函数返回机制,但还是有些不同
- 其一是函数fun()中的返回语句将控制权返回到调用fun的函数,但throw语句将控制器向上返回到第一个这样的函数,包含能够捕获相应异常的try-catch组合
- 另一不同之处,引发异常时编译器总是创建一个临时拷贝,即使异常规范和catch块中指定的是引用
C++异常主要目的是为了设计容错程序提供语言级支持,即异常使得程序设计中包含错误处理功能更容易,以免事后采取一些严格的错误处理方式,异常的灵活性和相对方便性激励着程序员在条件允许的情况下在程序设计中加入错误处理功能,总之,异常时这样一种特性:类似于类,改变您的编程方式
C++库定义了很多基于exception的异常类型
-
stdexcept异常类
-
logic error,描述了典型的逻辑错误,包括domain_error,invalid_argument,length_error,out_of_bounds
- 数字函数有定义域和值域,传入函数的参数不在定义域内会引发domain_error异常
- invalid_argument指出给函数传递了一个意料外的值
- length_error用于指出没有足够的空间来执行所需的操作
- out_of_bounds用于指示索引错误
-
runtime_error异常描述了可能在运行期间发生但难以预计和防范的错误
- underflow错误在浮点数计算中,存在浮点类型可以表示的最小非零值,计算结果比这个值还小时将导致下溢错误。整型和浮点型都可能发生上溢错误。计算结果可能不在函数允许的范围之内,但没有发生上溢或下溢错误,这种情况下,可以使用rang_error异常
-
-
bad_alloc异常和new
- 对于使用new导致的内存分配问题,引发bad_alloc异常,头文件new包含bad_alloc类的声明,它从exception类公有派生而来的,但以前,当无法分配请求的内存量时,new返回一个空指针
15.4 RTTI
C++有3个支持RTTI的元素
- 如果可能的话,dynamic_cast运算符将使用一个指向基类的指针来生成一个指向派生类的指针,否则,该运算符返回0–空指针
dynamic_cast<Type *>(pt) - typeid运算符返回一个指出对象的类型的值
typeid(类名) == typeid(*pg) - type_info结构存储了有关特定类型的信息
15.5 类型转换运算符
- dynamic_cast,该运算符的作用是,使得能够在类层次结构中进行向上转换,而不允许其他转换
dynamic_cast(expression) - const_cast,用于指向只有一种用途的类型转换,即改变值未const或volatile,其语法与dynamic_cast运算符相关,提供该运算符的原因是,有时候可能需要这样一个值,大多数时候是常量,但有时又是可以修改的
- reinterpret-cast运算符用于天生危险的类型转换,这样的转换适用于实现的底层编程技术,然而, reinterpret_case运算符并不指出所有的类型转换,可以将指针转换为足以存储指针表示的整型,但不能将指针转换为更小的整型或浮点型,另外,不能将函数指针转换为数据指针