虚函数表、虚指针、虚函数实现原理:
不采用虚函数时,若用父类的指针指向子类,则此指针是指向子类中父类的一部分,行为与父类相同。
虚函数表和虚指针:
class A
{
protected:
virtual void test(){cout<<"aaa"<<endl;}
virtual void test1(){cout<<"df"<<endl;}
};
其中,sizeof(A)=4,代表虚指针的大小,虚指针指向虚函数表,虚表里面才是存放的是虚函数的地址,虚指针存在于每个对象中;C++编译器将虚函数表的指针存储在对象实例中最前面的位置;虚表是整个类共用的,大小取决于虚函数的个数。
虚函数实现原理:如果一个类中存在虚函数,编译器会为该类创建一个虚函数地址表,保存自己类中虚函数的地址,并为该表创建一个指针,指向这个表,每个类的对象都会有一个vptr,该类的所有对象共用一个虚函数地址表。
调用虚函数时,系统会先获取对象的vptr,然后找到vbtl,找到相应的虚函数分析从而得出结果。
纯虚函数:
纯虚函数声明:virtual 函数类型 函数名 (参数表列) = 0;
(1)、纯虚函数没有函数体。
(2)、最后面的“=0”并不表示函数返回值为0,它只起形式上的作用,告诉编译系统“这是虚函数”。
(3)、这是一个声明语句。
包含纯虚函数的类,被成为是抽象类。抽象类不能使用new出对象,只有实现了这个纯虚函数的子类才能new出对象。
C++中的纯虚函数“只提供声明,没有实现”,是“接口继承”,它的作用时在基类中为其派生类保留一个函数的名字,以便派生类根据需要对他进行定义。
如果在派生类中没有对基类的纯虚函数进行定义,则该函数在派生类中仍然为纯虚函数。
动态绑定和静态绑定
首先需要明确几个名词定义:
静态类型:对象在声明时采用的类型,在编译期已确定。
动态类型:通常是指一个指针或引用目前所指对象的类型,实在运行期决定的。
静态绑定:绑定的静态类型,所对应的函数或属性依赖于对象的静态类型,发生在编译器。
动态版定:绑定的是动态类型,所对应的函数或属性依赖于对象的动态类型,发生在运行期。
比如常见的,virtual函数是动态绑定,non-virtual函数是静态绑定,缺省参数值也是静态绑定。
当virtual函数被调用时,编译器产生的代码直到运行时才能确定应该调用哪个版本的函数,被调用的函数是与绑定到指针或者引用上的对象的动态类型相匹配的一个;
绝对不要重新定义继承而来的非虚函数,因为这样导致函数调用由对象声明时的静态类型确定了,而和对象本身脱离了关系,这将给程序留下不可预知的隐患和BUG。
绝对不要重新定义一个继承二来的virtual函数的缺省参数值,因为缺省参数值都是静态绑定(为了执行效率),而virtual函数确是动态绑定。
因为缺省参数值是静态绑定,但是vitural函数是动态绑定,所以可能程序运行的结果不是我们想要的。
引用能否实现动态绑定?
可以,指针或引用是在运行期根据他们绑定的具体对象确定的。