跳转至

C++ 笔记 | 第5课 类的继承与派生

继承和派生

继承与派生 (Derivation and Inheritance) 是 C++ 的重要机制,是面向对象程序设计的重要特征。该机制自动为一个类提供来自另一个类的数据结构和操作。这样可以利用已构造好的类生成新类,充分利用已有资源,进行代码复用(code reused)。在构造新类的过程中保持已有类的特性称为继承。在已有类的基础上新增自己的特性而构造新类的过程称为派生

C++
// 如
class Rectangle: public Shape {...};

访问控制

继承性质 基类中成员 (函数) 的访问权限 派生类中成员 (函数) 的访问权限
public
public
protected
private
public
protected
private
protected
public
protected
private
protected
protected
private
private
public
protected
private
private
private
private

保护成员

对于建立它的类,它与 private 成员的性质相似; 对于继承此类建立的类,它与 public 成员性质相似

友元与继承

由于派生类不能直接访问基类中的私有成员,所以若一个派生类要直接访问基类中的私有成员,可以将这个派生类声明为基类的友元

访问权限调整

只能恢复原状, 不能调整访问权限

C++
1
2
3
public:
Myclass::f; // 恢复 f 的公有属性
Myclass::g; // 恢复 g 的公有属性

成员名限定

在派生类和基类中可以声明同名的成员(函数),不特别说明,在派生类中访问时,编译器会默认为是派生类中的(后出现的),除非用 基类名:: 基类成员名 进行成员名限定,才能调用同名的基类成员(函数)

派生类构造函数与复制构造函数

C++
1
2
3
4
5
6
// 若 B 是基类,A 是派生类,则派生类 A 的构造函数的书写格式为: 
A::A(参数表): B(参数){...} // 先构造基类无名对象 B

// 派生类 A 的复制构造函数的书写格式为:
A::A(const A &p): B(p){...} // 这里 B 的参数为 A 类对象 p,
// 用对象 p 中基类部分去复制构造无名基类对象 B

带基类内嵌对象的派生类

若有类 Point,私有成员为 x_, y_ 若要以 Point 作为基类,生成派生类 LineLine 中希望有两个 Point 对象 (用两个点来表示平面坐标上的一条直线),若想派生两次 Point 是不允许的. 在 Line 类中加一个 private 成员 Point p; 就行了(另法:Line 类不继承 Point 类, 私有成员为两个 Point, 函数重新写(因为没有继承))

动态束定与虚函数

virtual 所修饰的成员函数,被称为虚函数。

延迟到程序运行时进行的束定称为动态束定 (动态绑定)。

当一个类是从一个或多个类派生出来时,往往会出现多个函数名相同且参数个数和参数类型也完全相同的成员函数,分别实现不同的功能。但这些函数不是重载函数(重载函数必须参数个数或参数类型不同,可以静态束定),编译器无法在编译时静态束定到底是要调用哪一个成员函数,即使可以静态束定,往往也不一定符合编程者的真正意愿而导致结果错误

虚函数延迟到程序运行时再进行动态束定,由 this 指针决定调用 (通过虚函数表(vtable)) 哪一个同名的成员函数

需要在重名的成员函数前加上 virtual 声明

包含虚函数的类被称为多态类

多态性 (polymorphism) 是面向对象的重要特征之一

类型兼容性规则: 基类指针可以指向它的派生类对象, 反之错误。

在派生类中重定义虚函数时,必须满足:

  • 1 与基类的虚函数参数个数相同;
  • 2 与基类的虚函数参数一一对应;
  • 3 函数返回类型或修饰词 (如 const) 与基类的虚函数相同,或都返回指针或引用(可以不同类型)。

若满足上述三条件,有时可以省略 virtual 关键字, 编译器会自动认为是同族虚函数。

但从程序可读性角度考虑,建议保留 virtual 关键字

纯虚函数

  • 纯虚函数是抽象类中的虚函数,在函数名后直接赋值为 0, 无函数体。
  • 纯虚函数不同于空函数,是抽象的。
  • 纯虚函数为抽象类中的成员函数。
  • 包含纯虚函数的类被称为抽象类。
  • 抽象类 (无论类中是否有数据成员) 无法实例化,因此不能用抽象类定义对象。

纯虚函数声明格式: virtual 函数类型 函数名(参数表)=0;

C++
1
2
3
class Figure{ // Figure 是一个抽象类 
public:
virtual double getArea()=0;// 纯虚函数,为整个类族提供了通用的外部接口语义}; 

虚析构函数

Figure 类中加入虚析构函数 virtual ~Figure(){}~Figure() ~Rectangle() ~Triangle() 变为同族的虚析构函数。

虚析构函数是 C++ 中唯一的函数名不同的虚函数族。

注意: 有虚析构函数,但没有对应的虚构造函数。

虚析构函数使系统在执行过程中进行析构时,根据 this 指针所指的当前对象,动态决定使用这个对象的析构函数进行正确析构。(否则都掉用基类的析构函数无法执行派生类自己的析构函数导致析构不彻底)

多继承

C++
class D : public A, protected B, private C
{...}

多继承的二义性

若出现二义性 (ambiguous) 则需要在相应成员或成员函数前加基类名限定(如 A::)

虚基类

D 继承 B 和 C, B 和 C 同时继承 A, 那么 D 中会有两个 A 基类的副本(大多情况下不希望这样)

C++ 引入了虚基类(virtual base class), 虚基类将使得派生类 D 在继承间接共同基类 A 时只保留一个 A 的副本。

C++
1
2
3
4
5
6
7
8
class A
{...};
class B: virtual public A
{...};
class C: virtual public A
{...};
class D: public B, public C
{...};