目录
1. 继承
1.1 基础使用
1.2 构造函数
1.2.1 透传构造
1.2.2 委托构造
1.2.3 继承构造(熟悉)
1.3 对象的创建与销毁流程
1.4 多重继承
1.4.1 基础使用(掌握)
1.4.2 二义性
1.4.2.1 基类拥有同名成员
1.4.2.2 菱形继承(钻石继承)
1.5 权限
1.5.1 权限修饰符
1.5.2 不同权限的继承
1.5.2.1 公有继承
1.5.2.2 保护继承(掌握)
1.5.2.3 私有继承(掌握)
2. 多态
2.1 概念
2.2 函数覆盖
2.3 实现
2.4 原理
2.5 隐患
3. 类型转换 C++11
3.1 static_cast(掌握)
3.2 dynamic_cast
3.3 const_cast(熟悉)
3.4 reinterpret_cast(熟悉)
4. 抽象类 abstract class
4.1 概念
4.2 用法
4.3 多态
围绕继承与多态,对相关的面向对象知识点进行学习,这些内容也是最重要的内容。
继承就是在一个已经存在的类的基础上新建立一个类,新创建的类拥有之前类的特性。继承是面向对象的三大特性之一,体现了代码复用的思想。
- 已经存在的类被称为“基类 Base Class”或“父类”
- 新创建的类被称为“派生类”或“子类Sub Class”
下面是一个最简单的单一继承:
上面的继承在实际开发中没有任何意义,因为派生类对基类没有做出任何改变。通常派生类都会在基类的基础上做出一些代码修改或增加。
基类和派生类是相对的,一个类既可以作为基类又可以作为派生类,取决于两个类之间的关系。派生类是基类的具象化,基类是派生类的抽象化。
对于基类的私有成员,派生类可以继承,但是无法直接访问,访问需要通过基类提供的接口。
派生类的构造函数必须直接或间接调用基类的任意一个构造函数。如果程序员不手动在派生类的构造函数中调用基类的构造函数,编译器会尝试调用基类的无参构造函数。
编译器无法处理所有情况,例如基类没有无参构造函数。
上面的情况必须程序员手动在派生类的构造函数中直接或间接调用基类的构造函数:
- 透传构造
- 委托构造
- 继承构造(C++11)
注:构造函数和析构函数是不能被继承的,继承构造仅仅是一种用法的名称,并没有继承构造函数。
1.2.1 透传构造
透传构造属于在派生类的构造函数中直接调用基类的构造函数。

1.2.2 委托构造
委托构造本身可以脱离继承使用,指的是在某个类中构造函数A可以调用构造函数B。在继承中就可以让构造函数A调用构造函数B,构造函数B透传调用基类的构造函数,这样构造函数A就间接调用了基类的构造函数。

委托构造要避免委托闭环。
在上面代码中,程序会卡在第46行,因为构造函数执行不完,互相委托形成闭环。

1.2.3 继承构造(熟悉)
继承构造是C++11的新特性,并不是表示能继承构造函数,而是一种简便的写法,可以一句话实现一种透传构造。
在派生类中使用下面的语句,可以让派生类生成n(n为基类的构造函数数量)个构造函数,同时这n个构造函数参数与基类的n个构造函数相同,每个派生类的构造函数都透传参数相同的基类构造函数。


在上面的结果中可以得到如下规律:
1. 创建与销毁过程是对称的。
2. 静态的周期贯穿整个程序。
3. 创建过程中,先执行基类;销毁过程中,后执行基类。因为派生类依赖于基类。
虽然推荐理解记忆,但是也可以直接记忆,根据自己的学习习惯自行选择。
1.4.1 基础使用(掌握)
C++支持多继承,即一个派生类可以有多个基类。
1.4.2 二义性
1.4.2.1 基类拥有同名成员
当多重继承的两个基类拥有同名成员时,编译器会无法区分,因此出现二义性问题。
解决方法:在二义性的成员前使用 类名:: 修饰。
1.4.2.2 菱形继承(钻石继承)
如果一个类A有两个派生类B和C,类D同时继承B和C,此时就出现了菱形继承,当对象D调用A的成员时,会产生二义性。

解决方法1:使用类名::进行区分。
解决方法2:虚继承。
当使用虚继承时,Furniture类内部会生成一张虚基类表(程序运行时加载进内存),内部存储Furniture的成员的调用地址,每个Sofa和Bed类对象内部都会有一个隐藏成员指向虚基类表。
SofaBed对象同时继承了Sofa和Bed类的虚基类表指针,调用func()函数时查表比对,防止二义性的出现。

虚继承的本质是查表,因此会降低调用效率。
1.5.1 权限修饰符
本类中
派生类中
全局(例如主函数中)
private 私有(默认)
√
X
X
protected 保护
√
√
X
public 公有
√
√
√
1.5.2 不同权限的继承
三种权限修饰符可以修饰继承:
- 公有继承
- 保护继承
- 私有继承
1.5.2.1 公有继承
使用的最多的一种继承,之前的继承都是公有继承。在公有继承中,基类的所有成员均可以被派生类继承,但是基类的私有成员无法被派生类直接访问,基类的保护成员和公有成员继承到派生类中作为派生类的保护成员和公有成员(权限不变)。
1.5.2.2 保护继承(掌握)
在保护继承中,基类的所有成员均可以被派生类继承,但是基类的私有成员无法被派生类直接访问,基类的保护成员和公有成员继承到派生类中作为派生类的保护成员。
1.5.2.3 私有继承(掌握)
在私有继承中,基类的所有成员均可以被派生类继承,但是基类的私有成员无法被派生类直接访问,基类的保护成员和公有成员继承到派生类中作为派生类的私有成员。
从广义上讲,多态可以分为静态多态和动态多态。
静态多态(编译时多态)发生在程序的编译阶段,主要包括函数重载和运算符重载,在编译时就能确定调用关系。
动态多态(运行时多态),本章讨论的主要是动态多态。因此从狭义上讲,多态指的是动态多态。
多态(polymorphism)按照字面的意思是“多种状态”,简单的概括为“一个接口,多种状态”,一个函数接口,在运行时根据传入的参数类型执行不同的策略。
多态的实现需要有三个前提条件:
- 公有继承
- 函数覆盖(函数重写)override
- 基类引用/指针指向派生类对象
函数覆盖(函数重写)的前提是虚函数,虚函数使用关键字virtual修饰成员函数实现,普通的虚函数目的是实现函数覆盖。
虚函数的格式:
virtual 返回值类型 函数名 (参数表){}
一句话表达:在之前函数隐藏的前提下,把被隐藏的基类函数使用virtual修饰,就变成了函数覆盖。
虚函数具有以下性质:
- 在Qt Creator中斜体表示虚函数
- 虚函数具有传递性,基类被覆盖的虚函数会自动传递给派生类覆盖的新函数,使后者也变为虚函数。
- 成员函数和析构函数可以设置为虚函数,静态成员函数和构造函数不能设置为虚函数。
- 如果函数的声明与定义分离,virtual只需要修饰在声明处。
- C++11中,可以在派生类新覆盖的函数后面使用override关键字修饰,如果函数覆盖成功则不会报错。
多态往往伴随着函数的调用和传参,基类引用/指针指向派生类对象通常出现在函数传参中。
当使用多态时,上面代码中的Animal类对象会增加一个隐藏的成员指针,指向Animal类的虚函数表,虚函数表与之前的虚基类表类似,也是只有一份,属于Animal类持有,而非某个对象持有。
当Dog继承Animal时,也会有一张专属于Dog的虚函数表,这样表一开始会复制Animal的虚函数表。Dog对象内部会新增一个隐藏指针指向Dog的虚函数表,并且根据Dog的代码修改Dog的虚函数表。

动态类型绑定:当基类引用或指针指向派生类对象时,编译器内部会产生一段代码,用于检查对象的真正类型,这段代码会在程序运行时通过对象的隐藏指针指向的虚函数表找到调用地址。因此多态本质上也是程序在运行期间查表的过程,会降低程序的执行效率。
在使用多态时,可能会出现内存泄漏。
解决的方法是给基类设置虚析构函数,让析构函数也被虚函数表管理。
如果一个类是基类,建议把析构函数设置虚析构函数,因为编译器自动添加的析构函数没有virtual修饰。
C++支持C风格的强制转换,但是C风格的强制转换可能会有一些风险,让问题难以发现。
C++11提供了四种适用于不同场景的强制类型转换函数:
- static_cast 静态转换
- dynamic_cast 动态转换
- const_cast 常量转换
- reinterpret_cast 重解释转换
主要用于基本数据类型的转换。
static_cast没有运行时类型检查来保证转换的安全性,需要程序员判断转换是否安全。
static_cast还可以用于类层次结构中,即基类和派生类指针或引用的转换,但是还需要注意:
- static_cast进行上行转换是安全的,即把派生类指针或引用转换为基类的;
- static_cast进行下行转换是不安全的,即把基类指针或引用转换为派生类的;
上述转换只是可以,但不建议。
基于指针的转换:
基于引用的转换:
static_cast可以用于对象创建,但是参数必须与目标对象的构造函数相同。
dynamic_cast主要用于类层次之间的上行转换和下行转换。
- 上行转换与static_cast效果相同。
- 下行转换具有类型检查的功能,比static_cast更安全。

转换成功的示例:
转换失败的示例:
const_cast可以用于添加或移除对象(通常是指针或引用)的const修饰符。在正常情况下,应该尽量避免使用const_cast,而是考虑通过设计良好的接口或者其他手段来避免这样的转换。
reinterpret_cast可以把内存里的值重新解释,这种转换的风险极高,慎用!
抽象类只表达一个概念,并不与具体的对象联系,通常为它的派生类提供一个算法框架。需要注意的是,抽象类不光没有对象,也不能声明此类型,即抽象类型不能作为参数和返回值类型等。
纯虚函数是一种特殊的虚函数,这种函数名称定义只有声明。 纯虚函数的语法格式如下:
virtual 返回值类型 函数名(参数列表) = 0;
抽象类 ←→ 纯虚函数
如果一个类是抽象类,则内部必然有纯虚函数;如果一个类有纯虚函数,其必然是抽象类。
用法一:派生类继承抽象基类,把所有的纯虚函数覆盖并增加函数体。
用法二:派生类继承抽象基类后,没有把所有的纯虚函数覆盖并增加函数体,此时相当于继承了纯虚函数,此派生类也变为抽象类。
之前提到抽象类型不能被声明,但是由于抽象类支持多态,因此抽象类的指针和引用可以被声明。同时要注意因为支持多态,抽象类的析构函数都应该被设置为虚析构函数。
版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/cjjbc/15546.html