首先链接几个博客地址:C++ 虚函数表解析
cout << "虚函数表地址:" << (int*)(&b) << endl;
cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&b) << endl;
这个博客中cout << “虚函数表地址:” << (int*)(&b) << endl;这个说法是错误的,这个方法求出来的地址不是虚函数表的地址,而是对象b的首地址,也就是存放虚函数表指针的地址。
C++虚函数表学习心得之由类实例地址到虚函数表再到虚函数地址中各种地址解析
这个解释的较为详细。
说到虚函数,概念上非常简单,即:虚函数的地址存放于虚函数表之中。运行期多态就是通过虚函数和虚函数表实现的。类的对象内部会有指向类内部的虚表地址的指针。通过这个指针调用虚函数。下面记录以下概念理解的进阶版:如何从内存地址上进一步理解虚函数。
首先说明几个问题:函数指针定义:typedef void( *Fun )( void ); //是吧Fun定义为一个没有参数,返回void类型的函数指针(注意指针函数的区别)
虚函数表,虚表指针;
这是第二个博客中有关虚函数内存分配的图。首先定义相关虚函数:
class Base{
public:
virtual void f(){
cout<<"base f()"<<endl;
}
virtual void g(){
cout<<"base g()"<<endl;
}
};
class Derived:public Base{
public:
void f(){
cout<<"derived f()"<<endl;
}
void g(){
cout<<"derived g()"<<endl;
}
};
typedef void (*pFun)(void);
int _tmain(int argc, _TCHAR* argv[])
{
Derived d;
cout << "d的首地址的值:" << &d << endl;
cout << "d首地址内容:" << *(int*)&d << endl;
cout << "虚函数表首地址的值、虚函数表第一表项所在内存地址的值:" << (int*)*(int*)&d << endl;
cout << "虚函数表第一表项内容、Derived::f()函数首地址的值:" << *(int*)*(int*)&d << endl;
cout << "虚函数表第二表项地址的值:" << (int*)*(int*)&d + 1 << endl;
cout << "虚函数表第二表项内容、Derived::g()函数首地址的值:" << *((int*)*(int*)&d + 1) << endl;
pFun p = (pFun)(*(int*)*(int*)&d);
p();
cout << "函数指针指向的第一个地址p():" << p << endl;
p = (pFun)(*((int*)*(int*)&d + 1));
p();
cout << "函数指针指向的第二个地址p():" << p << endl;
int i;
cin >> i;
return 0;
}
结果
d的首地址的值:0118F808
d首地址内容:5872472
虚函数表首地址的值、虚函数表第一表项所在内存地址的值:00599B58
虚函数表第一表项内容、Derived::f()函数首地址的值:5837940
虚函数表第二表项地址的值:00599B5C
虚函数表第二表项内容、Derived::g()函数首地址的值:5837975
derived f()
函数指针指向的第一个地址p():00591474
derived g()
函数指针指向的第二个地址p():00591497
首先定义对象并获得对象的首地址,也就是&b,然后因为b这个对象中有虚函数,考虑到虚函数指针一般为int类型,取前4个字节(不同的平台不一样)为(int)&b(这个打印的地址和&b一样,都是首地址,顺便体会一下指针的含义),其中存放的内容就是虚函数的地址,需要对指针进行解引用,也就是*(int*)&b,(为什么不用&(int*)&b呢?,试了一下会报错,涉及到左右值的问题,&必须是左值(有地址)的才能用);
一定注意**(int*)&d和(int*)(int)&d*其实打印的值是一样的,只是前者用10进制表示,后者用16进制表示的。这里第二个和第三个的地址其实是一样的,只不过进制不同,所以才看来不同,实际上是一个地址。
通过函数指针方式获得的地址和通过取地址的方式获得的地址确实是一样的,不过还是一个是16进制(指针),一个是十进制(取地址);
另外如何从绝对地址执行程序?链接:从绝对地址执行程序