看下面这段代码
#include<iostream>
using namespace std;
struct A{
int num=10;
virtual void fun(){
cout<<"a:"<<num<<endl;
}
};
struct B:public A{
B(){num=20;}
virtual void fun(){
cout<<"b:"<<num<<endl;
}
};
int main() {
A*a=new B;
A b=B();
A c;
B d;
a->fun();
b.fun();
c.fun();
d.fun();
}
这段代码中发生了一次多态一次对象切片以及三次静态绑定,下面是对应主函数内的中间代码
main ()
{
void * D.36444;//临时变量,用于存储new B的返回值
struct B D.36464;//临时变量,用于存储B()的返回值
int D.40095;//临时变量,用存储主函数的返回值
{
struct A * a;
struct A b;
struct A c;
struct B d;
try
{
D.36444 = operator new (16);
try//执行new B为临时变量赋值
{
B::B (D.36444);
}
catch
{
operator delete (D.36444, 16);
}
a = D.36444;//临时变量为a赋值
B::B (&D.36464);//调用B()为临时对象赋值
try//调用切片构造函数只取D中A的成员为b的成员构造
{
A::A (&b, &D.36464.D.36395);
}
finally
{
D.36464 = {CLOBBER};//清除临时变量
}
_1 = &_ZTV1A + 16;//栈帧内指针移动预留出16字节空间,下面三行相当于无参构造内容
c._vptr.A = _1;
c.num = 10;
B::B (&d);//定义了构造函数调用对应的构造函数
_2 = a->_vptr.A;//取出a的虚表指针
_3 = *_2;//定位到虚表
OBJ_TYPE_REF(_3;(struct A)a->0) (a);//RTTI机制,根据虚表选择对应的虚函数,这里的0是偏移量(或者可以理解为索引)
A::fun (&b);//下面三个都是静态绑定
A::fun (&c);
B::fun (&d);
}
finally//清除临时变量
{
b = {CLOBBER};
c = {CLOBBER};
d = {CLOBBER};
}
}
D.40095 = 0;
return D.40095;
}
需要注意的是在C++中使用指针调用虚函数时都会触发RTTI机制进行虚函数的选择,别的情况下都会采取静态绑定(别扯引用,引用的实现也脱离不了指针),所以说动态多态只能发生在父类对象或引用接收子类的过程中,子类对象构造父类对象的时候不会发生。
OBJ_TYPE_REF(_3;(struct A)a->0) (a);
这里a进行了强制类型转化,它的作用很关键,理解起来可以从下面这个例子入手。
struct A{
virtual void fun(int a=1){
cout<<"a:"<<a<<endl;
}
};
struct B:public A{
void fun(){
cout<<"B:"<<a<<endl;
}
virtual void fun(int a=2){
cout<<"b:"<<a<<endl;
}
};
int main(){
A*a=new B;
a->fun();
}
运行结果是b:1
而不会在选择的时候被B::fun()碰瓷,这就是因为声明了a的类型,然后它直到了去哪里找普通函数,从而避免了函数调用不明的情况。