C++的虚函数和多态性

来源:互联网 发布:python 入门教程 知乎 编辑:程序博客网 时间:2024/06/02 04:58
虽然对象不同但是调用的函数接口相同,使用时更有整体性和一致性,而不同的对象接收同一消息却有不同的行为,这种现象就是c++中的多态性。


其实c++的多态性,有两种表现,分别是编译时的多态性和运行时的多态性。


编译时的多态性:比如函数和运行符的重载,对象调用同一个接口函数(参数不同),会有不同的方法。编译后函数的符号由于参数的不同而不同,因此能定位到程序块地址。

运行时的多态性:通过基类引用或指针调用基类中定义的函数时,由于并不确定函数执行的对象,有可能是基类也有可能是派生类,所以需要通过后期绑定技术确定执行的函数。


运行时的多态性通过虚函数实现,虚函数必须存在于继承的环境下。基类的指针指向其派生类,用该指针调用类中的虚函数,则表现的是派生类的方法。那么虚函数是通过什么机制来确定当前对象调用的函数地址的呢?


其实虚函数机制也并不神秘,它只是对应的类有一张虚函数表(Virtual Table),在这个表里是虚函数的入口地址,在实例化一个对象时,对象内存空间的最前端指向这张表,这样保证高效的获取虚函数地址。当子类继承父类并重新定义了父类的虚函数时,子类的虚函数表中的虚函数地址就覆盖了父类的虚函数。那么当基类的引用(或指针)调用虚函数时,该引用从实际对象里获得虚函数表,在表里找到虚函数。由于引用的实际类型可能是基类也可能是子类,只有实际运行时才能确定,所以呈现出多态性。


示意图:


代码验证:

virtual_func.h

<span style="font-size:18px;">#ifndef __VIRTUAL_FUNC_H_#define __VIRTUAL_FUNC_H_class Shape{public:Shape(){};~Shape(){};virtual int perimeter();virtual int area();      };class Point : public Shape{public:Point(){};~Point(){};int coordinate();};class Rectangle : public Shape{public:Rectangle(){};~Rectangle(){};virtual int perimeter();virtual int area();};#endif</span>

virtual_func.cpp
<span style="font-size:18px;">#include <iostream>#include "virtual_func.h"int Shape::area(){std::cout << "shape->area\n" << std::endl;return 0;}int Shape::perimeter(){std::cout << "shape->perimeter\n" << std::endl;return 0;}int Point::coordinate(){std::cout << "Pint->coordinate\n" << std::endl;return 0;}int Rectangle::area(){std::cout << "rectangle->area\n" << std::endl;return 0;}int Rectangle::perimeter(){std::cout << "rectangle->perimeter\n" << std::endl;return 0;}</span>

main.cpp
<span style="font-size:18px;">#include <stdio.h>#include "virtual_func.h"typedef int(* Fun)(void);int main(){Shape shape_a;Point point_a;Rectangle rectangle_a;Rectangle rectangle_b;Shape *pshape;Fun pfun = NULL;int *virtual_T = NULL;pshape = &shape_a;virtual_T = (int *)*(int *)(&shape_a);pfun = (Fun)*(virtual_T+0);printf("class Shape, obj shape_a: VT:0x%x VF:0x%x\n", virtual_T, pfun);pfun();virtual_T = (int *)*(int *)(&point_a);pfun = (Fun)*virtual_T;printf("class Point, obj point_a: VT:0x%x VF:0x%x\n", virtual_T, pfun);pfun();virtual_T = (int *)*(int *)(&rectangle_a);pfun = (Fun)*virtual_T;printf("class Rectangle, obj rectangle_a: VT:0x%x VF:0x%x\n", virtual_T, pfun);pfun();virtual_T = (int *)*(int *)(&rectangle_b);pfun = (Fun)*virtual_T;printf("class Rectangle, obj rectangle_b: VT:0x%x VF:0x%x\n", virtual_T, pfun);pfun();}</span>

运行结果:


基类Shape                                   虚函数表地址:0x400ef0                                      虚函数perimeter地址:0x400a54

派生类Point ,没有重定义虚函数   虚函数表地址:0x401030                                      虚函数perimeter地址:0x400a54 (基类虚函数地址)

派生类Point ,重定义虚函数         虚函数表地址:0x400ed0                                       虚函数perimeter地址:0x4009c4 (Point类虚函数地址)

派生类Point ,重定义虚函数         虚函数表地址:0x400ed0 (一个类使用同一个表)  虚函数perimeter地址:0x4009c4 (Point类虚函数地址)









0 0
原创粉丝点击