第 13 章 类继承
13.1 一个简单的基类
使用 class Child : public Parent { ... }
进行公有派生,基类的公有成员将成为派生类的公有成员,基类的私有部分也将成为派生类的一部分,但只能通过基类的公有和保护方法访问。
创建派生类对象时,程序首先创建基类对象,构造函数表示方式为 Child::Child(const string &name, int age) : Parent(age) { ... }
。除非调用默认构造函数,否则应该显式地调用基类构造函数。释放对象时先执行派生类对象的析构函数,再执行基类的析构函数。
13.2 继承:is-a 关系
13.3 多态公有继承
可以在派生类中重新定义基类方法或使用虚方法实现多态。
如果要在派生类中重新定义基类的方法,通常将该函数声明为虚函数,这样程序将根据对象类型而不是引用或指针的类型决定选择方法的版本。
声明虚析构函数可以确保释放派生对象时按正确的顺序调用析构函数。
13.4 静态联编和动态联编
将源代码中的函数调用解释为执行特定的代码块成为函数名联编。在编译过程中联编称为静态联编,在运行时联编称为动态联编。静态联编效率更高。编译器对虚函数使用动态联编。
将派生类引用或指针转换为基类引用或指针被称为向上强制转换,这是允许的,但向下强制转换是不允许的,除非使用显式类型转换。
按值传递将仅传递基类部分,按引用传递则可以实现隐式向上转换。
在基类中声明的 virtual
虚函数可以使该方法在基类和所有派生类中是虚的,同时也应该在所有派生类中重新定义的函数方法声明 virtual
。
构造函数不能是虚函数,派生类不继承基类的构造函数,这样做没有意义;友元函数不能是虚函数,因为它不是类成员函数。析构函数应该是虚函数,除非它不用做基类,这样可以保证对象被正确析构。如果派生类没有重新定义函数,则将使用函数的基类版本。
如果重新定义继承的方法,应该确保与原来的原型完全相同,但如果返回类型是基类,则可以修改返回类型为派生类,这种特性被称为返回类型协变。如果基类声明被重载了,则应该在派生类中重新定义所有的版本,如果只重新定义一个版本,则其他版本将被隐藏,派生类对象无法使用它们。
13.5 访问控制:protected
在类外只能通过共有类成员访问 protected
中的类成员,和 private
一致,但派生类的成员可以直接访问基类的 protected
成员。
最好对类成员数据采用 private
访问控制,对部分成员函数使用 protected
访问控制。
13.6 抽象基类
使用纯虚函数提供没有实现的函数,声明方法为 virtual void Function( ... ) = 0;
。
包含纯虚函数的类只能作为基类,它也不能被实例化。
13.7 继承和动态内存分配
假设基类使用了动态内存分配,派生类不用 new
关键字动态分配内存,则不需要定义显式析构函数、复制构造函数和赋值运算符,调用基类对应的函数不会有副作用。但当派生类使用 new
时则需要显式定义这些函数。
友元函数不是成员函数,因此无法被继承,可以使用强制类型转换派生类为基类再使用友元函数。