标签:fun 变量 变量定义 类型 并且 name 直接 using vptr
在讲原理之前,首先介绍静态联编和动态联编:
静态联编:再编译阶段确定程序执行的代码,比如普通的变量定义int a = 10,以及函数重载等
动态联编:在程序运行过程中确定程序执行的代码,比如条件判断语句
多态的原理:
当一个类具有虚函数时,编译器会自动为这个类在全局区中的常量区添加一个虚函数表,这个虚函数表存储着这个类的所有虚函数的入口地址,并且会为这个虚函数表增加一个vptr指针(也叫虚指针)用于指向这个表,且这个指针会被放在属性列表首位;vptr指针是在编译阶段由编译器自动加入的,所以在写代码时是找不到这个指针的
当通过父类指针调用子类对象的方法时,编译器只知道这个指针是父类型的指针,并不知道指向的是子类对象,所以先查找父类的此方法是否是虚函数,如果不是虚函数,编译器会直接确定这个函数的地址(静态联编),程序在执行时直接执行此函数并不会进行查表操作,当发现调用的父类的这个函数是虚函数时,编译器会自动跳过,等待程序运行时再确定调用哪个函数(动态联编),当程序运行时,调用此方法时发现函数地址没有确定,此时程序会通过子类对象虚指针查找子类的虚函数表,如果子类复写了父类的虚函数,那么这个复写的函数会被放入子类的虚函数表,进而会被查找到然后调用,如果没有复写父类的虚函数,那么父类的虚函数会被继承下来,然后放入子类的虚函数表中,进而也会被查找到然后执行,值的注意的是,虚指针可以被继承且复写父类的某个虚函数时,无论加不加virtual关键字,编译器都会自动补上virtual关键字
以下面代码为例:
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> using namespace std; class Parent{ public: virtual void fun1(){ cout << "parent fun1()..." << endl; } void fun2(){ cout << "parent fun2()..." << endl; } }; class Child :public Parent{ public :
void fun1(){//这里不加virtual关键字,编译器也会自动加上,然后将此函数放入其虚函数表中 cout << "child fun1()..." << endl; } virtual void fun2(){ cout << "child fun2()..." << endl; } }; void test(Parent* obj) { obj->fun1();//如果父类fun1不是虚函数,这句话函数的地址已经固定了,程序执行时不会再查虚函数表,如果父类fun1是虚函数,这句话fun1函数地址不确定,程序会进行查表 obj->fun2();//父类的fun2不是虚函数,直接确定了fun2的地址,不会进行查表 } int main(void) { Parent* obj = new Child(); test(obj); system("pause"); return 0; }
标签:fun 变量 变量定义 类型 并且 name 直接 using vptr
原文地址:https://www.cnblogs.com/songtothesun/p/9942165.html