C++类的继承

1、派生类定义
image
①一个派生类,可以同时有多个基类,这种情况称为多继承

②一个派生类只有一个直接基类的情况,称为单继承
image
③在派生过程中,派生出来的新类也可以作为基类再继续派生新的类

④一个基类可以同时派生出多个派生类

⑤在类组中,直接参与派生出某类的基类称为直接基类,基类的基类甚至更高层的基类称为间接基类

如果不显式地给出继承方式关键字,系统的默认值就认为是私有继承

派生类成员是指除了从基类继承来地所有成员外,新增加的数据和函数成员

2、派生类生成过程

①吸收基类成员

  • 派生类中包含了它的全部基类中除构造函数和析构函数外的所有成员。
  • 在派生过程中,构造函数和析构函数都不能被继承

②改造基类成员

  • 两个问题:第一个是基类成员的访问控制:依靠派生类定义时的继承方式来控制。第二个是,对基类数据的覆盖和隐藏。
  • 隐藏:如果派生类声明了一个和某基类成员同名的新成员,派生的新成员就隐藏了外层同名成员。

③添加新的成员

  • 派生类新成员的加入是继承与派生机制的核心

3、公有继承

①当类的继承方式为公有继承时,基类的公有和保护成员的访问属性在派生类中不变,而基类的私有成员不可直接访问。

4、私有继承

①当类的继承方式为私有继承时,基类中的公有成员和保护成员都以私有成员身份出现在派生类中,而基类的私有成员在派生类中不可直接访问。也就是说基类的公有和保护成员被继承后作为派生类的私有成员,派生类的其他成员可以直接访问它们,但是在类族外部通过派生类的对象无法直接访问它们。

5、保护继承

①保护继承中,基类的公有和保护成员都以保护成员的身份出现在派生类中,而基类的私有成员不可直接访问。这样,派生类的其他成员就可以直接访问从基类继承来的公有和保护成员,但在类外部通过派生类的对象无法直接访问它们。

6、类型兼容原则

①类型兼容原则是指在需要基类对象的任何地方,都可以使用公有派生类的对象来替代。

替代的具体情况:派生类的对象可以隐含转换为基类对象;派生类的对象可以初始化基类的引用;派生类的指针可以隐含转换为基类的指针。

7、派生类的构造函数只负责对派生类新增的成员进行初始化,对从基类继承下来的成员,其初始化工作还是由基类的构造函数完成。同样,对派生类对象的扫尾、清理工作也需要加入新的析构函数。

8、构造函数

①在构造派生类的对象时,会首先调用基类的构造函数,来初始化它们的数据成员,然后按照构造函数初始化列表中指定的方式初始化派生类新增的成员对象,最后才执行派生类构造函数的函数体。
image
②如果对基类初始化时,需要调用基类的带有形参表的构造函数时,派生类就必须声明构造函数。

③派生类构造函数执行的一般顺序

  • 调用基类构造函数,调用顺序按照它们被继承时声明的顺序(从左向右)
  • 对派生类新增的成员初始化,初始化顺序按照它们在类中声明的顺序
  • 执行派生类的构造函数的函数体

④C++11中,派生类能够重用其直接基类定义的构造函数。一个类只初始化它的直接基类,一个类只继承其直接基类的构造函数。派生类继承基类构造函数的方式是提供一条注明了直接基类名的using声明语句。
image
【注】通常情况下,using声明语句只是令某个名字在当前作用域可见。一个构造函数的using声明不会改变该结构函数的访问级别。

  • 如果派生类定义的构造函数与基类的构造函数具有相同的参数列表,则该构造函数将不会被继承,定义在派生类中的构造函数将替换继承而来的构造函数。
  • 默认、复制和移动构造函数将不会被继承。

9、复制构造函数

①如果要为派生类编写复制构造函数,一般需要为基类相应的复制构造函数传递参数。
image
【注】Base类的复制构造函数参数类型应该是Base类对象的引用,怎么这里用Derived类对象的引用v作为参数呢?这是因为类型兼容规则在这里起了作用:可以用派生类的对象去初始化基类的引用。因此,当函数的形参是基类的引用时,实参可以是派生类的对象。

10、析构函数

①在派生过程中,基类的析构函数也不能继承下来,如果需要析构,就要在派生类中声明新的析构函数。

②派生类的析构函数只要在函数体中负责把派生类新增的非对象成员的清理工作做好就够了。系统会自己调用基类及对象成员的析构函数来对基类及其对象成员进行清理。但它的执行次序与构造函数正好相反,首先执行析构函数的函数体,然后对派生类新增的类类型的成员对象进行清理,最后对所有从基类继承来的成员进行清理。

11、作用域分辨符

①作用域分辨符就是“::”,它可以用来限定要访问的成员所在的类的名称
image
②如果派生类中声明了与基类成员函数同名的新函数,即使函数的参数表不同,从基类继承的同名函数的所有重载形式也都会被隐藏。如果要访问被隐藏的成员,就需要使用作用域分辨符和基类名来限定。

③只有相同的作用域中定义的函数才可以重载。

④将using用于基类中的函数名,这样派生类中如果定义同名但参数不同的函数,基类的函数将不会被隐藏,两个重载的版本将会并存在派生类的作用域中。

⑤如果某个派生类的部分或全部直接基类是从另一个共同的基类派生而来,在这些直接基类中,从上一级基类继承来的成员就拥有相同的名称,因此派生类中也就会产生同名现象,对这种类型的同名成员也要使用作用域分辨符来唯一标识,而且必须引用直接基类来进行限定。

12、虚基类

①当某类的部分或全部直接基类是从另一个共同基类派生而来时,在这些直接基类中从上一级共同基类继承而来的成员就拥有相同的名称。在派生类的对象中,这些同名数据成员在内存中同时拥有多个副本,同一个个函数会有多个映射。我们可以使用作用域分辨符来唯一标识并分别访问它们,也可以将共同基类设置为虚函数,这时从不同的路径继承过来的同名数据成员在内存中就只有一个,同一个函数名也只有一个映射。

解决同名成员的唯一标识问题:设置虚基类
image
②Base0有数据成员var0,由Base0公有派生产生了Base1和Base2,派生过程中声明Base0为虚基类,再由Base1和Base2作为基类公有派生Derived。使用了虚基类之后,在派生类Derived中只有唯一的数据成员var0。在建立Derived类对象的模块中,直接使用“对象名.成员名”的方式就可以唯一标识和访问这些成员。

③如果虚基类声明有非默认形式的构造函数,并且没有声明默认形式的构造函数,这时,在整个继承关系中欧给,直接或间接继承虚基类的所有派生类,都必须在构造函数的成员初始化列表中列出对虚基类的初始化。

④建立一个对象时,如果这个对象中含有从虚基类继承来的成员,则虚基类的成员是由最远派生类的构造函数通过调用虚基类的构造函数进行初始化的。而且,只有最远派生类的构造函数会调用虚基类的构造函数,该派生类的其他基类对虚基类构造函数的调用都被自动忽略。

构造一个类的对象的一般顺序是:

  • 如果该类有直接或间接地虚基类,则先执行虚基类的构造函数。
  • 如果该类有其他基类,则按照它们在继承声明列表中出现的次序,分别执行它们的构造函数,但构造过程中,不再执行它们的虚基类的构造函数。
  • 按照在类定义中出现的顺序,对派生类中新增的成员对象进行初始化。对于类类型的成员对象,如果出现在构造函数初始化列表中,则以其中指定的参数执行构造函数,如果未出现,则执行默认构造函数;对于基本数据类型的成员对象,如果出现在了构造函数的初始化列表中,则使用其中指定的值为其赋初始值,否则什么也不做。
  • 执行构造函数的函数体。
posted @ 2025-04-14 16:08  风归去  阅读(56)  评论(0)    收藏  举报