六一献礼——动态绑定解密
作者:chence 时间:2012/6/1
献给我的老婆...愿她像小朋友一样健康、快乐...
程序1:
#include <iostream>
using namespace std;
#include <windows.h>
class A{
protected:
string m_s;
public:
A(string s):m_s(s){}
public:
void virtual show()
{
MessageBoxA(NULL, m_s.c_str(), "A", MB_OK);
}
};
class B:public A{
public:
B(string s):A(s){}
public:
void virtual show()
{
MessageBoxA(NULL, m_s.c_str(), "B", MB_OK);
}
};
int main()
{
A a("This is class of A");
B b("This is class of B");
a.show();
b.show();
A *p, *pa = new A("This is a object of A via new!");
p = pa;
pa->show();
pa = new B("This is a object of B via new!");
pa->show();
delete p, pa;
return 0;
}
在main函数第5行下个断点,执行完这个函数后,看看pa指针
pa指向的内存包含一个A类的虚表指针。pa->show()调用的是A类的show()函数。
执行完第8行后:
可以看出pa的地址没有变化,变化的是pa的值,也就是pa指向的内存。pa其实指向的是一个B类对象,包含的虚表指针也是指向B类虚表的。故通过pa->show()调用的是B类的show()函数。
综上,通过pa调用show函数是动态的,这得看pa指向的哪个类。这就是多态的核心所在吧...
程序2:
#include <iostream>
using namespace std;
class A
{
public:
A(int a):val_A(a){}
A():val_A(10){}
void fa()
{
cout << "this is func_fa of class A." << endl;
}
void virtual f()
{
cout << "Virtual func_f of A" << endl;
}
private:
int val_A;
};
class B:public A
{
public:
B(int b):val_B(b){}
B():val_B(11){}
void fb()
{
cout << "this is func_fb of class B." << endl;
}
void virtual f()
{
cout << "Virtual func_f of B" << endl;
}
void virtual h()
{
cout << "Virtual func_h of B" << endl;
}
private:
int val_B;
};
int main()
{
A* pa = new A();
pa->f();
B* pb = new B();
pb->f();
delete pa,pb;
return 0;
}
程序输出:
Virtual func_f of A
Virtual func_f of B
在main函数的第3行下个断点,F5进入调试。执行第3句..
看看Local窗口各变量的内存:
先看pa,pa指向A类的一个对象,该对象包含一个虚表指针_vfptr和私有成员val_A.虚表指针类型为const A::’vftable’,虚表包含的虚函数类型为A::f(void).
pb,pb指向B的一个对象,该对象包含A类的一个对象和私有成员val_B. A类的对象包含一个虚表指针_vfptr和val_A,注意虚表指针类型为const B::’vftable’,虚表包含的虚函数类型为B::f(void).
由此可见派生类对象的内存组成为:父对象+ 子类自身的新的成员变量。父对象组成为:子类的虚表指针(如果有虚函数的话) +父类的成员变量。虚表里的函数指针规则为:如果子类重写了某个函数,则虚表里的对应的函数指针就指向子类重写的函数,否则就指向父类的那个函数。
对主函数作些许修改:
int main()
{
A* pa = new A();
pa->f();
B* pb = (B*)pa;//新建一个B类指针,并将pa赋给它
pb->f();
delete pa,pb;
pa = new B();
pa->f();
pb = (B*)pa;
pb->f();
delete pa,pb;
return 0;
}
程序输出:
Virtual func_f of A
Virtual func_f of A
Virtual func_f of B
Virtual func_f of B
执行完第三句的时候,看看pa和pb指针:
B* pb =(B*)pa;这个语句就是将pa所指的地址强制转换成B类指针,但是地址所指内存的内容却没有发生任何变化。本来new的一个对象是A,故属于对象的内存只包括一个虚表指针和A类的一个成员变量val_A=0xa。但是现在强制转换成了B类指针,因此会将紧跟后面的4个字节和前面A类对象组合成一个完整的B类对象。因为后面的4个字节没有初始化,故为一个非法值(0xfdfdfdfd)。所以用pb指针调用f()的时候,调用的实际上是A类的f().
我们还注意到pa和pb其实指向同一个对象,后面delete了两次却没有出现问题,说明delete运算符会检查是否已经释放。这和free()函数有很大区别。
接下来new了一个B对象,并将其指针赋给pa。后面又将pa转换成B类指针赋给pb。
看下这两个指针:
可以看到从new了开始,内存空间就已经分配和初始化好了。由于new的是一个B类对象,故虚表指针就指向一个B类的虚表。不管后面将该地址强制转化成一个什么对象指针,内存的数据不会变。虽然pa是一个A类指针,当把B类指针赋给它的时候,它调用的虚函数却是B类的虚函数。所以调用的函数不跟指针的类型相关,而是看它指向什么类型对象。