友元类
和友元函数一样,友元类具有原始类的访问权限,可以访问其私有成员和保护成员。友元类的关系不同于is-a或has-a关系,友元类可以让看起来互无关系的两个类具有关系,比如遥控器和电视机。
声明友元类的位置无关紧要,在private或public都可以,但是由于友元类或访问原始类的对象,所以原始类的声明必然在友元类之前,面对更复杂的情况可以使用前置声明。
有些时候并不需要整个类都声明为友元,只将部分函数声明问友元成员函数。注意使用域解析符。
friend void Remote::set_channel(Tv& tv,int c);
假设TV类就有这样一个友元成员函数,这样它就要首先Remote是一个类才能正确编译,所以Remote类应在TV类之前,但是set_channel又使用了TV类的私有成员,这代表着TV类又必须在Remote类之前。而这是不可能做到的。
使用前置声明避免问题发生
class TV;
class Remote{};
class TV{};
不能反过来声明,因为TV类声明的是友元成员函数,它必须看到这个成员函数的声明才行。前置声明只负责告诉编译器这是一个类,而对类中的方法一无所知,直到它的定义出现。
假如Remote类中也要使用TV类中的方法怎么办?
答案是暂时在Remote类中申明函数原型,然后再TV类后面定义。
class TV;
class Remote{};
class TV{};
return-type Remote::function(...){}
使用inline关键字可以根据需要变成内联函数。
嵌套类
在类中声明结构,类都是嵌套类。
嵌套类最重要的是认识到他的访问权限。
- 首先要看类是否可见。
- 如果嵌套类再私有字段,那么只有声明它的类知道它的存在。
- 保护字段则是声明它的类和派生类,外部不可见。
- 公有字段则都可见。
- 之后看嵌套类的访问控制
- 对于嵌套类外面的部分,都看作外部。比如说声明它的类可见嵌套类,但依旧不能访问嵌套类的私有字段和保护字段。因为对于嵌套类来说,声明它的类已经是外部了。
异常
- 将对象作为异常类型,我们可以构造一个异常类(STL就是这样做的)能够更方便我们关系异常的行为。再触发异常时将该类的对象抛出。
- 栈解退:不同于函数返回,再函数抛出异常后,程序会直接跳到第一个匹配异常对象的catch块,将在这其中创建的对象全部释放掉,对于自定义类程序会自动调用析构函数。
- 异常总是会创建一个临时拷贝的异常对象,这是因为在函数结束的时候,这个异常对象同样也会被释放,所以创建副本给调用异常处理程序是必要的。但是我们依然能看到catch块后的异常匹配类型依旧是引用,这是为了让派生类对象和基类匹配。
- 如果你想具体的触发某种异常类,则catch块的配列顺序应该与派生顺序相反,否则它会率先和基类对象匹配而输出基类的信息。
- 使用catch(…)来接收任何的异常。
RTTI(运行阶段类型识别)
RTTI:Runtime Type Identification,运行阶段识别,旨在为程序在运行阶段确定对象类型提供一种标准方式。
如果我们希望程序在运行时候调用对应的函数,在类层次结构中所有成员都拥有虚函数,则不需要RTTI的支持。但派生对象可能包含不是继承而来的方法,或者想要调试跟踪目标对象,RTTI提供了支持。
C++提供了三个支持RTTI的元素
- 使用dynamic_cast运算符。
- typeid运算符
- type_info结构存储信息。
RTTI只适用于包含虚函数的类。
dynamic_cast运算符
dynamic_cast指出“是否可以安全地将对象的地址赋给特定类型的指针”,用法为:
dynamic_cast<type-name> (expression)
dynamic_cast类似于一种类型转换,它可以判断是否可以安全的在基类和派生类指针之间转换,如果安全——派生类指针赋值给基类,那么将该指针转换为基类或间接基类指针,如果不安全——基类转为派生类,则返回null。
对于引用,由于没有类似于空指针的空引用,所以引用在转换失败时会抛出bac_cast异常。
typeid和tpye_info
typeid运算符返回一个type_info的引用,而type_info重载了==和!=,所以你可以很方便的判断两个类类型是否相同。
typeid(Magnificent)==typeid(*pg)
如果pg是一个空指针,将引发bad_typeid异常。
你可以调用type_info的方法来访问查看类型的信息,比如name()会打印类的名字。
类型转换运算符
- dynamic_cast
- const_cast
- static_cast
- reinterpret_cast
const_cast
const_cast<type-name>(expression) //type-name must same as expression's type
const_cast可以让类型在const或volatile之间转换,const_cast只能在指针或引用之间使用。
注意,const虽然可以改变指针const的标签,但是假如指针指向的值是常量,那么你依旧无法改变该值。