C++的继承、封装和多态

C++的继承、封装和多态

这题超纲了 柳筋

C++面向对象的三种方式

封装

在C++里面,可以用class来定义一个类,而这个类与C语言中static很像,它们都可以让用户自己定义一个数据类型,不同的是,类中还可以有函数,而类中的数据又有三种类型,分别为public(公共的),protect(保护的),private(私有的)。

  1. 封装可以隐藏实现细节,使得代码模块化,使代码和功能独立

  2. 封装是把函数和数据包含在一个类里,数据只能通过函数或可以信任的对象进行访问,对不可信的对象进行隐藏

继承

继承:一个类可以使用另外一个类的所有功能,并在无需重新编写的情况下对继承过来的类进行补充扩展。

通过继承而来的类又被称为“子类”,被继承的类被称为“父类”或者“基类”。

继承的三种实现方式

  1. 实现继承:直接使用基类的属性和方法而无需额外编码

  2. 接口继承:指仅仅使用属性和方法的名称,但是子类必须提供代码的实现

  3. 可视继承:指子窗体(类)使用基窗体(类)的外观和实现代码的能力

单一继承

子类只继承了一个父类,这种情况称为单一继承,一般情况下都使用单一继承,使用多重继承容易造成混乱

多重继承

一个子类继承了多个父类,类与类之间要用逗号隔开,类名之前要有继承权限,假使两个或两个基类都有某变量或函数,在子类中调用时需要加类名限定符如c.a::i = 1;

菱形继承

假设有一个基类A,他有两个子类B和C都继承了A中的某个函数并且都进行了重写,此时又有一个子类用多重继承了B和C,那么此时就有出现一种情况,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//  菱形继承 菱形继承是多继承的一种特殊情况。
//菱形继承的问题:从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题。

class A
{
public:
int m_a = 1;
};
class B :public A
{
public:
int m_b = 2;
};
class C :public A
{
public:
int m_c = 3;
};
class D :public B, public C
{
public:
int m_d = 4;
};
void main()
{
D d;
d.m_d = 40;
d.m_c = 30;
d.m_b = 20;
//.m_a = 10;// 二义性
// 不能够访问 因为B 和C分别继承了A的m_a
//但是D 继承了B和C的m_a 所以D不能够分辨m_a到底是谁的
d.B::m_a = 100;
d.C::m_a = 200;
// 这样的话 就造成了m_a 有两个空间 一个B的100 一个C的200
}

如何解决菱形继承的问题呢?

  1. 最简单的,就是尽量不要出现菱形继承这种情况。

  2. 如果一定会用到菱形继承的话,可以用虚基类来解决,关于什么是虚基类,往下接着看吧

  3. 虚基表的引入也可以解决这个问题

  4. 详细可见:http://t.csdn.cn/goF1f

多态

一个接口多种方法,即用同一个接口,但是效果不同

静态多态

是在编译期就把函数链接起来,此时即可确定调用哪个函数或模板,静态多态是由模板和重载实现的,在宏多态中,是通过定义变量,编译时直接把变量替换,实现宏多态。

模板

template 定义一个模板。

详情请见:http://t.csdn.cn/QJqBk

重载
  1. 重载又有函数重载和运算符重载。

  2. 函数重载详情请见:http://t.csdn.cn/iwGFT

  3. 运算符重载请见:http://t.csdn.cn/vMRYM

动态多态

是指在程序运行时才能确定函数和实现的链接,此时才能确定调用哪个函数,父类指针或者引用能够指向子类对象,调用子类的函数,所以在编译时是无法确定调用哪个函数。

使用时在父类中写一个虚函数,在子类中分别重写,用这个父类指针调用这个虚函数,它实际上会调用各自子类重写的虚函数。

1
2
3
输出:
call child func
call ~base
虚函数

用virtual关键字修饰的函数;

本质上由虚指针和虚表控制,虚指针指向虚表中的某个函数入口地址,就实现了多态

详细可见:http://t.csdn.cn/CJs8B

构造函数可以是虚函数吗?

答:不可以,因为我们知道有虚函数的类在创建实例的时候会通过构造函数创建虚函数表,当调用虚函数的时候需要通过虚指针指向虚函数来调用,那么问题来了,当我们创建一个构造函数是虚函数的实例的时候,需要调用构造函数,而构造函数又因为是虚函数需要有虚函数表,那么此时就矛盾了,所以构造函数不可以是虚函数。

析构函数可以是虚函数吗

析构函数可以是虚函数,且常常如此

这个就好理解了,因为此时 vtable 已经初始化了;况且我们通常通过基类的指针来销毁对象,如果析构函数不为虚的话,就不能正确识别对象类型,从而不能正确销毁对象。

多态存在的三个必要条件

  1. 继承

  2. 重写

  3. 父类引用指向子类对象

实现多态的方法

  1. 面向对象有了一个重要的概念就是对象的实例,对象的实例代表一个具体的对象,所以肯定有一个数据结构来保存这一实例的数据,这一数据包括对象变量成员,如果对象有虚函数方法或存在虚函数的话,则还有对应的虚函数或虚表指针,不包括其他函数指针

  2. 虚函数在c++中的实现机制就是用虚表和虚指针,是每个类用了一个虚表,每个类的对象用了一个虚指针。要讲虚函数机制,必须讲继承,因为只有继承才有虚函数的动态绑定功能

  3. 详细可见上面的虚函数的知识

这篇博客有点水,等有时间了鼠鼠会重新写一次的,因为鼠鼠最近在看操作系统的东西,它太抽象啦!!!!所以可能鼠鼠要花很久的时间,哎,鼠鼠好菜的,鼠鼠好想找个实习呀,鼠鼠想赚钱,鼠鼠想去看周董演唱会,鼠鼠还想去西藏玩,鼠鼠想做的事情好多啊,但是鼠鼠只能躲在小小的小窝里,因为鼠鼠知道鼠鼠的能力还不够让上层社会的人看到鼠鼠,所以鼠鼠也有在努力啊,可是学历就这样,鼠鼠真的有机会卷得过别人嘛,鼠鼠不知道,鼠鼠只知道第二天要到了,鼠鼠要睡觉了,晚安~

  • 标题: C++的继承、封装和多态
  • 作者: 这题超纲了
  • 创建于: 2023-02-28 23:16:40
  • 更新于: 2023-06-23 14:37:28
  • 链接: https://qx-gg.github.io/2023/02/28/blog8/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
推荐阅读
浙江宇视科技一面 浙江宇视科技一面 C++中的一些设计模式 C++中的一些设计模式 C++11新特性 C++11新特性
 评论