C++多态

### 思考题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)
 

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值