Primer Plus 类继承
派生一个类
使用公有派生,基类的公有成员将成为派生类的公有成员,基类的私有部分也将成为派生类的一部分,但只能通过基类的公有和保护方法访问
1 | class RatedPlayer : public TableTennisPlayer { |
创建派生类时,基类先被创建,派生类过期时,先析构派生类,再析构基类
1 |
|
派生类的构造函数总是调用一个基类构造函数,可以使用初始化列表语法指明要使用的基类构造函数,否则将使用默认的基类构造函数
1 | derived::derived(t1 x, t2 y) : base(x, y) { |
基类指针可以指向派生类对象,基类引用也可以引用派生类对象,但只能用于调用基类方法
继承是is-a关系
多态公有继承
如果要在派生类中重新定义基类的方法,通常应将基类方法声明为虚的,这样,程序将根据对象类型选择方法版本,而不是根据指针或引用的类型
虚析构函数同样,根据对象类型选择调用对应的虚构函数,而不是根据指针或引用的类型,确保了析构函数序列的正确
静态联编和动态联编
函数名联编:将源代码中的函数调用解释为执行特定的函数代码块
静态(早期)联编:在编译过程中进行的联编
virtual关键字导致使用哪一个函数在编译时无法确定,所以编译器必须生成能够在程序运行时选择正确的虚方法的代码,这是动态(晚期)联编
编译器对非虚方法使用静态联编,对虚方法使用动态联编
为什么默认静态联编
效率更高,由C++的指导原则不要为不使用的特性付出代价,因此在需要虚函数时再考虑动态联编
虚函数的工作原理
有虚函数的类有一个虚函数表,比如基类有一个虚函数表,派生类有另一个虚函数表
每个对象都有一个隐藏成员,是一个指向虚函数表的指针
虚函数表里存了所有为类对象声明的虚函数的地址
如果派生类重新定义了基类虚函数,虚函数表里存的是新函数的地址
如果派生类没有重新定义基类虚函数,虚函数表里存的是函数原始版本的地址
如果派生类新定义了一个虚函数,虚函数表里会增加这个函数的地址
虚函数表存在只读数据段里
注意事项
构造函数不能是虚的
析构函数应该是虚函数,除非类不作为基类
友元不能是虚函数
派生类重新定义将隐藏基类方法
1 | // 重新定义继承的方法不是重载,无论参数列表是否相同 |
如果重新定义继承的方法,且返回类型是基类引用或指针,则可以修改为指向派生类的引用或指针
1 | // 返回类型协变 |
访问控制:protected
protected和private的区别只在派生类中体现,派生类的成员可以直接访问基类的保护成员
抽象基类(ABC)
纯虚函数提供未实现的函数
1 | virtual double Area() const = 0; // 纯虚函数的结尾处为 =0 |
当类声明中包含纯虚函数时,不能创建该类的对象
ABC描述的是至少使用一个纯虚函数的接口
可以把ABC看作是一种必须实施的接口