前言
🎡前面几篇文章介绍了C++面向对象的基础,本篇将介绍面向对象最重要
的部分。
🎃它是所有采用面向对象思想的编程语言的核心:封装、基础和多态
。
🎉面向对象思想需要通过实践
学习,仅仅通过文字描述很难理解它的本质。
封装
🎡封装指将数据成员私有化
,通过成员函数
的形式访问数据成员。
🎃封装的特点是降低各模块间的耦合度、提高可维护性,避免错误的发生
。
下面通过一个例子说明为什么需要封装:
1 |
|
输出结果:
1 | -1 |
🎡首先,直接对数据成员进行访问,不利于模块独立化,也不利于后期的维护。
🎃其次,对age数据成员赋值-1显然不符合逻辑。
采用封装思想,可以改为下面的代码:
1 |
|
输出结果:
1 | 年龄范围错误,请输入大于0的数字 |
🎃通过封装,我们给外界预留接口
,使用接口的人不需要知道具体的实现细节
。
类的友元
C++提供了友元机制
,可用来破坏数据封装和隐藏
。
封装是一个优越的机制,为了不破坏封装和隐藏原则,不推荐
使用友元机制。
友元函数
🎡函数或其它类的成员函数可以声明为类的友元,这样的函数叫做友元函数
。
🎈友元函数可以访问类内部的私有成员。
❗友元函数虽然在类中声明,但他不是类的成员函数。
函数声明为类的友元:
1 | friend 返回类型 函数名(参数列表); |
其它类的成员函数声明为类的友元函数:
1 | friend 返回类型 其它类名::函数名(参数列表); |
友元类
我们可以把一个类声明为另一个类的友元类。
如果类B对类A频繁的数据访问,并且由于类A的private的限制,类B只能通过public的成员函数进行访问。
这时候,不妨让类B成为类A的友元类。这样,类B的成员函数就全部成为类A的友元函数。
类B就可以直接访问类A的private成员了。
友元类定义格式:
1 | class A |
继承
🎃继承是指在原有类的基础上进行拓展或重写
部分成员,以提高代码利用率。
🐾原有类即基类或父类
,继承原有类的叫做派生类或子类
。
🎡派生类拥有基类的所有成员
,并可以增加新成员
。
我们可以新增基类中存在的成员,达到覆盖重写
的目的,隐藏
基类的成员。
注意:派生类不能继承基类的构造函数、析构函数和赋值运算符重载
。
派生类定义方法:
1 | class 派生类名:继承方式1 基类1, 继承方式2 基类2 |
😃C++中允许一个派生类同时继承多个基类
。
下面,通过例子说明继承:
1 |
|
输出结果:
1 | 跑 |
新增成员
🎡我们可以在派生类中新增
成员,达到拓展的目的。
通过例子说明:
1 |
|
输出结果:
1 | 跑 |
隐藏成员
🎡我们可以在派生类中重写基类的成员达到覆盖重写
的目的,隐藏
基类成员。
下面通过例子说明:
1 |
|
输出的结果:
1 | 跑到更快了 |
继承方式
🎡除了public、protected和private
成员外,在派生过程中,还会出现一种不可访问
成员。
🎃不可访问成员被隐藏
,在类内和类外均不能访问。
公有继承
使用public继承方式:
1 | class 派生类 : public 基类 |
公有继承对基类成员访问权限:
基类成员 | 派生类成员 |
---|---|
public成员 | public成员 |
protected成员 | protected成员 |
private成员 | 不可访问成员 |
不可访问成员 | 不可访问成员 |
🎃如果我们想某个成员只能在类内访问
,并且可以被子类继承
。可以给定它为protected
。
🎡peotected可以理解为可以被继承的private
。
私有继承
使用private
继承方式:
1 | class 派生类 : private 基类 |
公有继承对基类成员访问权限:
基类成员 | 派生类成员 |
---|---|
public成员 | private成员 |
protected成员 | private成员 |
private成员 | 不可访问成员 |
不可访问成员 | 不可访问成员 |
保护继承
使用protected
继承方式:
1 | class 派生类 : protected 基类 |
公有继承对基类成员访问权限:
基类成员 | 派生类成员 |
---|---|
public成员 | protected成员 |
protected成员 | protected成员 |
private成员 | 不可访问成员 |
不可访问成员 | 不可访问成员 |
public继承
使用最频繁,无论何种继承方式,private和不可访问成员
在派生类中均为不可访问成员
。
公有继承中,public成员和protected成员保留原有访问属性
。
私有继承中,public成员和protected成员访问权限变为private
。
保护继承中,public成员和protected成员访问权限变为protected
。
派生类的构造函数
派生类的构造函数有两个功能:
- 调用基类构造函数完成基类数据成员初始化。
- 初始化派生类新增的数据成员
派生类构造函数定义:
1 | 派生类名::派生类构造函数(参数列表):基类构造函数(参数列表) |
🎡如果基类定义了带参数
的构造函数,必须为派生类定义构造函数。
🎃如果基类没有定义带参构造函数,派生类构造函数可以省略。
😄C++11中提供了继承基类构造函数
的方法,使用using
语句继承。有兴趣可自行查阅资料。
派生类的析构函数
编译器先调用派生类的析构函数
,然后调用子类的析构函数
。
派生类的析构函数可以省略
,系统提供默认的析构函数。
如果在子类中创建了内存空间
,则需要定义
析构函数用于回收空间。
多重继承
当世界从滥用继承从吃到苦头时,组合成了大家关注的问题。
”能够组合就尽量不要用继承“,这是<<Java编程思想>>的名言。
继承,是一种高度的耦合,派生类和基类被紧紧的绑在一起,灵活性大大降低。
而且,滥用继承,也会使继承树变得又大又复杂,很难理解和维护
因此,本文不再讲解多重继承,有兴趣可以自行查阅资料。
多态
虚函数
🎡在介绍多态前,我们先来了解一下虚函数
。
虚函数(Virtual Function)即用Virtual
修饰的成员函数
。
虚函数用于多态,所在类一般是基类。派生的子类重写虚函数
(子类可省略Virtual)。
纯虚函数
🎃纯虚函数除了使用virtual
修饰,还需要在函数声明后加 = 0
。
🎡纯虚函数的作用:基类可以仅仅给出纯虚函数的声明
,而不给出具体定义。
🎈由继承它的派生类
完成对纯虚函数的定义。
纯虚函数声明的格式:
1 | virtual 返回类型 函数名(参数列表) = 0; |
抽象类
包含纯虚函数的类即抽象类
。
🎡由于包含的纯虚函数没有具体定义
,仅仅预留接口。所以是抽象的。
🧨因此,当然也不能创建对象。只能作为基类被派生类继承。
多态的定义
🎡多态是指基类指针或引用
指向派生类对象
。
🎃实际上,派生类对象转换
为了基类对象,这就是隐式的向上转型
。
既然转换了,基类指针或引用只能访问继承自基类的成员
。
下面通过一个例子说明:
1 |
|
输出的结果:
1 | 动物在跑 |
🎃可以看出,尽管我们在派生类中重写
了run函数,将基类的run函数隐藏
。
🎈但是,这里调用run函数时,还是调用了基类的run函数
。
😋我们实际上想调用的是重写后的run函数
,否则,多态就显得毫无意义了。
🎃这时,我们就用到了虚函数
。我们重写虚函数达到覆盖基类函数
的目的。
下面通过例子说明:
1 |
|
输出的结果:
1 | 狗在跑 |
😋通过重写虚函数,我们覆盖而不是隐藏
了基类的函数。
多态的优势
🎡基类中声明虚函数
,派生类中重写覆盖
虚函数,通过基类指针或引用
访问派生类对象
叫做多态。
通过多态,提供了公共接口
,降低模块间的耦合度
,增强后期可维护性和拓展性
。
虚析构函数
🎡因为基类的指针和引用
只能访问继承自基类的成员
,所以编译器默认情况下调用基类的析构函数
回收内存。
🎃如果我们使用派生类对象申请内存空间
,则同样需要调用派生类的析构函数
。
😋这就用到了:虚析构函数
。
在基类的析构函数前加virtual
关键字,就可以声明虚析构函数。
这样,在对象销毁时,编译器即调用基类的析构函数
也调用派生类的析构函数
。
总结
🎡本节介绍了C++面向对象思想最重要的部分:封装、继承和多态。
🎃此外,还补充了友元机制的相关知识。
🚩创作不易,本人保证所发文章均为精心筹备。
💌如需转载,请保留作者信息和博客地址。
📡如果感觉博客对你略有帮助,欢迎转发给你的朋友,让他们加入到技术风暴中来吧!