接上文。
虚函数、虚表在没有实例的情况下是无法从语法层面进行访问的。
那么其到底有没有生成呢?
#include<iostream>
using namespace std;
class A
{
private:
int x;
long long xy;
int y;
public:
virtual void f1(void)
{
cout<<"A::f1"<<endl;
}
virtual void f2(void)
{
cout<<"A::f2"<<endl;
}
};
class B
{
private:
int x;
long long xy;
int y;
public:
virtual void f1(void)
{
cout<<"B::f1"<<endl;
}
virtual void f2(void)
{
cout<<"B::f2"<<endl;
}
};
class C
{
private:
int x;
long long xy;
int y;
public:
virtual void f1(void)
{
cout<<"C::f1"<<endl;
}
virtual void f2(void)
{
cout<<"C::f2"<<endl;
}
};
class D
{
private:
int x;
long long xy;
int y;
public:
virtual void f1(void)
{
cout<<"D::f1"<<endl;
}
virtual void f2(void)
{
cout<<"D::f2"<<endl;
}
};
void normal_func1(){};
void normal_func2(){};
void normal_func3(){};
int global_int = 0;
int global_uni;
int main()
{
static int static_int = 0;
cout<<"(long*)normal_func1 "<<(long*)(normal_func1)<<endl;
cout<<"(long*)normal_func2 "<<(long*)(normal_func2)<<endl;
cout<<"(long*)normal_func3 "<<(long*)(normal_func3)<<endl;
cout<<"(long*)(&global_int) "<<(long*)(&global_int)<<endl;
cout<<"(long*)(&static_int) "<<(long*)(&static_int)<<endl;
cout<<"(long*)(&global_uni) "<<(long*)(&global_uni)<<endl;
cout<<"global_uni "<< global_uni<<endl;
B virtual_instance1;
A virtual_instance0;
D virtual_instance3;
cout<<endl<<endl<<"虚函数相关: "<<endl<<endl;
cout << "(long*)(&virtual_instance0) " << (long*)(&virtual_instance0) << endl;
cout << "(long*)(&virtual_instance1) " << (long*)(&virtual_instance1) << endl;
cout << "(long*)(&virtual_instance3) " << (long*)(&virtual_instance3) << endl;
cout << "(long*)*(long*)(&virtual_instance0) " << (long*)*(long*)(&virtual_instance0) << endl;
cout << "(long*)*(long*)(&virtual_instance1) " << (long*)*(long*)(&virtual_instance1) << endl;
cout << "(long*)*(long*)(&virtual_instance3) " << (long*)*(long*)(&virtual_instance3) << endl;
cout << "*(long*)*(long*)(&virtual_instance0) " << hex<<"0x"<<*(long*)*(long*)(&virtual_instance0) << endl;
cout << "*(long*)*(long*)(&virtual_instance1) " << hex<<"0x"<<*(long*)*(long*)(&virtual_instance1) << endl;
cout << "*(long*)*(long*)(&virtual_instance3) " << hex<<"0x"<<*(long*)*(long*)(&virtual_instance3) << endl;
A virtual_instance00;
int x = 0;
cin>>x;
if(x)
{
C *pC = new C();
// C *pC = (C*)new D();
}
cout << "(long*)(&virtual_instance00) " << (long*)(&virtual_instance00) << endl;
cout << "(long*)*(long*)(&virtual_instance00) " << (long*)*(long*)(&virtual_instance00) << endl;
cout << "*(long*)*(long*)(&virtual_instance00) " << hex<<"0x"<<*(long*)*(long*)(&virtual_instance00) << endl;
cout << "(long*)(&virtual_instance0) " << (long*)(&virtual_instance0) << endl;
cout << "(long*)(&virtual_instance1) " << (long*)(&virtual_instance1) << endl;
cout << "(long*)(&virtual_instance3) " << (long*)(&virtual_instance3) << endl;
cout << "(long*)*(long*)(&virtual_instance0) " << (long*)*(long*)(&virtual_instance0) << endl;
cout << "(long*)*(long*)(&virtual_instance1) " << (long*)*(long*)(&virtual_instance1) << endl;
cout << "(long*)*(long*)(&virtual_instance3) " << (long*)*(long*)(&virtual_instance3) << endl;
cout << "*(long*)*(long*)(&virtual_instance0) " << hex<<"0x"<<*(long*)*(long*)(&virtual_instance0) << endl;
cout << "*(long*)*(long*)(&virtual_instance1) " << hex<<"0x"<<*(long*)*(long*)(&virtual_instance1) << endl;
cout << "*(long*)*(long*)(&virtual_instance3) " << hex<<"0x"<<*(long*)*(long*)(&virtual_instance3) << endl;
typedef void(*Fun)(void);
Fun pFunc1, pFunc2, pFunc3;
pFunc1 = (Fun)*((long*)*(long*)(&virtual_instance0));
pFunc1();
pFunc2 = (Fun)*((long*)*(long*)(&virtual_instance1) + 1);
pFunc2();
pFunc3 = (Fun)*((long*)*(long*)(&virtual_instance3) + 1);
pFunc3();
return 0;
}
输出:
(long*)normal_func1 0x55e10f66bcfa
(long*)normal_func2 0x55e10f66bd01
(long*)normal_func3 0x55e10f66bd08
(long*)(&global_int) 0x55e10f86e25c
(long*)(&static_int) 0x55e10f86e268
(long*)(&global_uni) 0x55e10f86e260
global_uni 0
虚函数相关:
(long*)(&virtual_instance0) 0x7ffcaa24d2e0
(long*)(&virtual_instance1) 0x7ffcaa24d2c0
(long*)(&virtual_instance3) 0x7ffcaa24d300
(long*)*(long*)(&virtual_instance0) 0x55e10f86dd18
(long*)*(long*)(&virtual_instance1) 0x55e10f86dcf8
(long*)*(long*)(&virtual_instance3) 0x55e10f86dcd8
*(long*)*(long*)(&virtual_instance0) 0x55e10f66c782
*(long*)*(long*)(&virtual_instance1) 0x55e10f66c7f2
*(long*)*(long*)(&virtual_instance3) 0x55e10f66c862
等待cin>>阻塞中。
整理:
可以看到,A::f1()-B::f1()-D::f1()
之间的地址差,有个两倍的差距。如果将代码中的
if(x)
{
C *pC = new C();
// C *pC = (C*)new D();
}
将C *pC = new C();
删除,用下面一句,则A::f1()-B::f1()-D::f1()
之间的地址差是一致的。
结论:
1 . 根据代码中是否出现实例化代码(不管有没有运行到这个地方),决定是否生成虚函数。
2 . 虚表的生成顺序与类虚函数的生成顺序正好相反,猜测是编译器使用类似栈的结构,每次生成类的虚函数都会入栈(应该是按类定义的顺序?)。
3 . 如果没有出现实例化的代码,即使有类似"C* pC;"
的代码,也不会生成虚函数与虚表。
4 . 无法修改虚表,若强行修改会出现段错误。
新的问题:
1 . 全局变量、局部static变量的出现顺序似乎只与其定义的顺序有关。且未初始化的全局变量、局部static变量似乎也是与其放在一块,并没有分开放置,是因为直接生成的可执行文件吗(链接库就会分开以节省空间?)?还是说有不同的实现?
2 . Ubuntu下64位的情况下,虚表是在全局区之前、虚函数之后,虚函数是在普通函数之后;但是在windows下其地址反而是最高的,堆空间的地址好像也没有什么规律。