### 思考题1
```cpp
#include <iostream.h>
class A {
protected:
int x;
public:
A() { x = 1000; }
virtual void print() { cout << "x=" << x << "\t"; }
};
class B : public A {
int y;
public:
B() { y = 2000; }
void print() { cout << "y=" << y << '\n'; }
};
```
#### 问题:
若将 `virtual` 省去,程序运行结果如何?
#### 分析:
1. **有 `virtual` 关键字时**:
- `print` 是虚函数。
- 通过基类指针或引用调用 `print` 时,会调用派生类的实现,即使基类指针指向的是派生类对象。
- `pa->print();` 会根据 `pa` 指向的对象的实际类型来调用相应的 `print` 方法。
- 如果 `pa` 指向 `A` 对象,调用 `A` 的 `print` 方法。
- 如果 `pa` 指向 `B` 对象,调用 `B` 的 `print` 方法。
- 这个就是动态联编(编译时候不确定调用基类的还是派生类的),得运行时看pa指向哪个对象调用谁
1. **没有 `virtual` 关键字时**:
- `print` 不是虚函数。
- 通过基类指针或引用调用 `print` 时,会调用基类的实现。
- 调用哪个版本的 `print` 方法仅取决于指针的静态类型(编译时类型)。
- ==在编译时,编译器只知道变量的类型和它的可能分配。但编译器无法预测运行时的实际情况,因为对象的实际指向可能会在程序运行期间多次改变。,无法预见运行时 `pa` 会指向哪个对象==
- 无论 `pa` 指向的是 `A` 对象还是 `B` 对象,`pa->print();` 都会调用 `A` 的 `print` 方法
因此,去掉 `virtual` 关键字后,程序的行为不再依赖于 `pa` 指向的对象的实际类型,而是完全取决于 `pa` 的静态类型(即它被声明为 `A*`)。所以即使 `pa` 指向 `B` 对象,它仍然会调用 `A` 类的 `print` 方法
![[Pasted image 20240529180135.png]]
![[Pasted image 20240529180115.png]]
如果改为 `pa->A::print();`,那么无论 `pa` 指向的是 `A` 类型对象还是 `B` 类型对象,都将调用 `A` 类中的 `print` 方法,绕过了多态性。
### 思考题2
![[Pasted image 20240529181919.png]]
![[Pasted image 20240529181935.png]]
![[Pasted image 20240529182211.png]]
### 多态的实现
![[Pasted image 20240529183128.png]]
#### 1.静态多态
运算符重载 函数重载
![[IMG_9363.png]]
#### 2.动态多态
![[IMG_9364.png]]
![[IMG_9365 1.png]]
动态多态的原理:vptr指向vtbl(vtable虚函数表),然后通过vtbl去找到那个虚函数实现
---
#### 这部分可省略不看:
如果只有一个类可以多态:(多态性没有真正表现出来由于没有派生类)
![[IMG_9362.png]]
多态性并没有真正展示出来(因为没有派生类),但虚函数通过基类指针或引用调用的机制本身就是多态性的体现。如果有派生类重写 `myvirfunc` 并且基类指针指向派生类对象时,多态性就会生效。
---
#### 2.动态多态的实现
如果有继承关系:(一般都得是通过基类的指针或引用来调用派生类的函数,这才算是多态性)
![[IMG_9361.png]]
下面是运行多态,动态多态,有继承关系的一般多态
![[IMG_9365 2.png]]
![[IMG_9366 1.png]]
![[IMG_9368.png]]
满足赋值兼容规则,
如果不为虚函数,则都会采用静态联编(谁的指针就去执行谁的函数,shape的指针就去执行shape的函数)都是调用shape里的函数show
把show 和area改为虚函数
运行时才知道Shape的指针s4是指向派生类,所以去调用circle派生类的show
Shape的s5引用等于s2(基类的对象),那就会调用shape基类的show
![[IMG_9367.png]]
会出现调用派生类虚函数的是:
**基类的指针指向派生类对象
基类的引用等于派生类的对象
基类的引用/指针与基类联系没用
### 虚析构函数
![[IMG_9370.png]]
![[IMG_9372.png]]
![[IMG_9371.png]]
采用静态联编,pb是base的指针,那么删除就是调用base的析构函数,只是释放了整型空间
![[Pasted image 20240529182438.png]]
在base的析构函数上面写virtual,虚函数具有向下传递性,derived的析构函数也是虚的,可以写上virtual 可以不写
![[IMG_9373.png]]
![[IMG_9374.png]]
![[IMG_9382 1.jpeg]]
### 纯虚函数
![[IMG_9381.jpeg]]
![[IMG_9375.png]]
p的area去调用什么要看传入什么对象给他,不同对象传给我调用不用函数 ,实现了多态(用基类的指针),并在运行时确定
![[Pasted image 20240529184724.png]]
shape的area就是为了后面派生类的各种实现提供一个统一的接口用的,不需要实现什么功能
![[IMG_9376.png]]
等于0是声明他是一个纯虚函数,没有具体的功能实现,不能被调用。
后面的派生类去提供具体的功能实现(重写)
![[IMG_9378.png]]
如果没有提供实现,那么circle也不能实例化对象 circle s是错误的,因为area还是一个抽象函数
有纯虚函数的叫做抽象类,抽象类不能实例化对象 shape s是错误的
![[IMG_9379.jpeg]]
![[IMG_9382.jpeg]]
final 和 override
在添加父类虚函数后面添加final代表不能再被重写
![](https://img-blog.csdnimg.cn/bf0d27fd873a4019a1c214901633da48.png)
final修饰类,代表不能被继承
![](https://img-blog.csdnimg.cn/c13d87fda2fb41efbbfbf31fa10ac4d7.png)
override代表必须要重写虚函数,如果没有重写便会报错
![](https://img-blog.csdnimg.cn/56b4224f23d742c8841f89b5639cfdf6.png)