一 应用场景
举个不太精确的例子,假设我们要射击一个拳击游戏,该游戏有好多角色,泰森、阿里、刘易斯或者霍利菲尔德;这些角色都具备一个共同的函数——出圈。但是不同的选手出拳的速度和力量不同,因此产生的效果不同。
二 示例演示及结果说明
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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| #include<iostream> using namespace std; class poser { protected: int age; public: virtual void beat() const {cout << "一般选手的力量为260磅" << endl;}
}; class Ali: public poser { virtual void beat() const {cout << "阿里一拳的力量为420磅" << endl;} }; class Lewis: public poser { virtual void beat() const {cout << "刘易斯一拳的力量为480磅" << endl;} }; class Tyson: public poser { virtual void beat() const {cout << "泰森一拳的力量为500磅" << endl;} }; class Holy: public poser { virtual void beat() const {cout << "霍利菲尔德一拳的力量为350磅" << endl;} }; int main() { poser *p[5]; poser *p1; int choice, i; for (int i = 0; i < 5; i++) { cout << "(1)阿里(2)刘易斯(3)泰森(4)霍利菲尔德:"; cin >> choice; switch (choice) { case 1: p1 = new Ali; break; case 2: p1 = new Lewis; break; case 3: p1 = new Tyson; break; case 4: p1 = new Holy; break; default: p1=new poser; break; } p[i]=p1; p[i]->beat(); } return 0; }
|
2.2 输出结果
1 2 3 4 5 6 7
| (1)阿里(2)刘易斯(3)泰森(4)霍利菲尔德:1 阿里一拳的力量为420磅 (1)阿里(2)刘易斯(3)泰森(4)霍利菲尔德:2 刘易斯一拳的力量为480磅 (1)阿里(2)刘易斯(3)泰森(4)霍利菲尔德:5 一般选手的力量为260磅 (1)阿里(2)刘易斯(3)泰森(4)霍利菲尔德:
|
2.3 代码说明
- 为了让不同的拳手(对象)调用相同的出拳(名字相同的函数)产生不同的效果(函数运行效果),我们将基类的beat()函数说明为virtual,也就是虚函数。因此程序执行时会自动判断对象,从而调用不同对象中的函数,该函数将会覆盖基类的同名函数
2.4 过程说明
- 我们在main函数中假如了switch语句,来判断用户的每次输入,然后根据输入的数字在堆中创建不同派生类的对象(拳手)
- 由于堆中的对象都是匿名的,而指向它们的指针(p1)只有一个,为了正确地找到它们,我们还要定义一个数组来接收每个指针(p1),因为对象(拳手)太多,不好区分,这里用数组a来接收每个指针(该指针p指向每个拳手)。最后通过数组的每个元素(指向不同拳手的指针)访问该对象(拳手)的函数(出拳)
2.5 注意点
- 由于该程序运行不可能预测到用户输入哪个数字,是1、2、3还是4或者5,因此也不可能预先知道要调用哪个对象的函数,指针p是在我们输入一个数时才指向某个对象的。这就叫做动态联编或者运行时联编,相反假如再运行之前就确定好哪个指针指向哪个对象,而且在运行时不能更改的叫静态联编或者编译时联编。静态联编由于对象不用对自身进行跟踪,因此速度浪费比较小,而动态联编虽然可以动态追踪对象,灵活性比较强,但是速度浪费比较强,但是速度浪费也很严重。这就是两者的区别
2.6 知识点
- 一个函数被说明为虚函数,在派生类中覆盖了该函数,那么该函数也是个虚函数,不过你也可以把它说明为虚函数,这样看起来更好懂些