CPP学习之——继承是否可以实现多态性(13.4)

一 概述

本节主要通过示例演示多态产生的问题,延伸到动态联编和静态联编及修改实验的结果,保证结果的正确

二 示例演示及结果输出

2.1 代码

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
#include<iostream>
using namespace std;
class A
{
public:
virtual void print() {cout << "a" << endl;}
};
class B: public A
{
public:
void print() {cout << "b" << endl;}
};
class C: public A
{
public:
void print() {cout << "c" << endl;}
};
int main()
{
A a;
B b;
C c;
// a.print();
// b.print();
// c.print();
A *p=&a;
A *p1=&b;
A *p2=&c;

p->print();
p1->print();
p2->print();

return 0;
}

2.2 输出结果

1
2
3
a
b
c

2.3 代码说明

  • 两者之间为什么会有这么显著的区别呢?这是因为在不使用virtual之前,C++对重载的函数使用静态联编,而使用了virtual以后,C++则对该函数进行了动态联编,那么什么是动态联编,什么又是静态联编呢

三 动态联编和静态联编

3.1 概念

  • 将一个调用函数者联结上正确的被调用函数,这一过程叫做函数联编,一般简称为联编
  • C++中的联编共分两种,就是静态联编和动态联编

3.2 静态联编

  • 因此在未加virtual说明时,该函数时静态联编,即被调函数和调用函数者的关系以及它们的内存地址在编译时都已经确立好,运行时不再发生变化
  • 这样的好处是速度快,因为运行的时候不用对各个对象的函数进行追踪,只需要传递参数、执行确定好的函数并在函数调用完毕后清理内存即可。
  • 因此我们看到在第一例中由于基类的print函数未被说明为虚函数,则该函数在执行的时候将采用静态联编,即不对重载的各个对象的函数进行追踪,这导致C++编译器在编译时认定的指向基类的三个指针p、p1、p2在运行时也不会根据对象的改变而发生改变。因此就算是将三个对象的内存地址依次赋给了三个指针,三个指针还是默认指向基类。

3.3 动态联编

  • 与其相反,动态联编就要牺牲掉一些速度,因为每个函数调用在运行前是不可确立的,要随着用户的操作来执行相应的函数
  • 比如说在拳击游戏中用户按下一个出拳键,那么系统将根据用户选择的角色不同二采用不同的出拳(函数),虽然出拳这个动作名时相同的(函数名相同),但是他们产生的效果不一样的,有点拳手力量大,有的拳手力量小(函数的功能不宜,根据对象来定)
  • 这个函数调用就是不可预测的,因为你无法实现预料到用户选择的是哪个角色,这就要代码堆每个角色进行追踪,并且在游戏运行时要时刻地判断该调用哪个角色的拳头(合适的函数),然后再调用它,虽然这要比较灵活,但是相应地也就大大地增加了系统的开销。不过在这个游戏中使用动态联编却是个非常好的选择