一. 非多态分类:
C++Primer 第三版中文版Page752有如下记载:
“当成员函数是虚拟的时候,通过一个类对象(指针或引用)而被调用的该成员函数,是在该类对象的动态类型中被定义的成员函数。”
这句话有两个元素至关重要,
a.虚成员函数
b.类对象的指针或引用
而此两点有无的组合和有四种情况:
只有a,b同时符合才能实现动态联编(((Father*)lpFson)->DisplayV()结果参考),
而其他三种情况均不符合,编译器在编译期间将确定调用函数。
编译器也是根据此两点来确定到底是否动态联编。
下面对其他三种情况非动态联编进行说明举例。
1)子类覆盖父类函数时,指向子类对象的指针强制转换成指向父类的指针,然后调用此覆盖函数时,不进行动态动态绑定。也就是调用的是父类的函数,而非子类的函数((Father*)lpFson)->Display();结果参考)。【a不符】
2)子类覆盖父类函数时,子类对象强制转换成父类,然后调用此覆盖函数时,不进行动态动态绑定。也就是调用的是父类的函数,而非子类的函数(((Father)son).DisplayV();结果参考)。【b不符】
3)子类隐藏父类函数时,子类对象强制转换成父类,然后调用此隐藏函数时,不进行动态动态绑定。也就是调用的是父类的函数,而非子类的函数(((Father)son).Display();结果参考)。【a,b都不符】
代码验证:
class Father | ||||||||
{ | ||||||||
public: | ||||||||
Father(int i) | ||||||||
{ | ||||||||
var1 = i; | ||||||||
} | ||||||||
public: | ||||||||
~Father(void){}; | ||||||||
void Display() | ||||||||
{ | ||||||||
TRACE("Father::Display var1 = %d/n",var1); | ||||||||
} | ||||||||
virtual void DisplayV() | ||||||||
{ | ||||||||
TRACE("Father::DisplayV var1 = %d/n",var1); | ||||||||
} | ||||||||
private: | ||||||||
int var1; | ||||||||
}; | ||||||||
//class Son | ||||||||
class Son : public Father | ||||||||
{ | ||||||||
public: | ||||||||
Son(int i,int j):Father( i) | ||||||||
{ | ||||||||
var2 = j; | ||||||||
} | ||||||||
public: | ||||||||
~Son(void){}; | ||||||||
void Display() | ||||||||
{ | ||||||||
TRACE("Son::Display var2 = %d/n",var2); | ||||||||
} | ||||||||
virtual void DisplayV() | ||||||||
{ | ||||||||
TRACE("Son::DisplayV var2 = %d/n",var2); | ||||||||
} | ||||||||
private: | ||||||||
int var2; | ||||||||
int var3; | ||||||||
}; | ||||||||
//调用呼出函数 | ||||||||
void CTianTestDlg::OnBnClickedCancel2() | ||||||||
{ | ||||||||
Son son(1,2); | ||||||||
son.Display(); | ||||||||
son.DisplayV(); | ||||||||
((Father)son).Display(); //输出:Father::Display var1 = 1 | ||||||||
((Father)son).DisplayV(); | //输出:Father::DisplayV var1 = 1 | |||||||
//指针运行情况 | ||||||||
TRACE("----------------------------------/n"); | ||||||||
Son *lpFson = &son; | ||||||||
lpFson->Display(); | ||||||||
lpFson->DisplayV(); | ||||||||
((Father*)lpFson)->Display();//输出:Father::Display var1 = 1 | ||||||||
((Father*)lpFson)->DisplayV();//输出:Son::Display var1 = 1 | ||||||||
} |
二. 非多态实现
1.简单理解上
编译器根据元素a和b很容易判断出来是静态编译,然后根据此时调用函数的定义类型就可以决定调用哪个函数,(进行过强制转换,就是转换后的的类的函数,未转换过的话,就是定义时候的类型)
而如果是动态编译,将根据指向的实在对象里记录的虚拟表指针,在动态运行时查虚拟表确定函数。
2.从编译器生成的汇编代码的验证:
请参考:
浅析C++中虚函数的调用及对象的内部布局
http://blog.csdn.net/starlee/archive/2008/02/13/2089358.aspx
里面有详细的汇编代码说明
<注>作为例外,类的构造函数在符合a,b条件时不进行动态联编,因为这个时候子类的对象还没有创建好,无法实现动态联编。