c++虚函数表中的内存布局
- 1.Class的内存分布
- 2.其他修改Class中变量的方法
- 3.通过虚函数表内存偏移调用虚函数
- 4.继承状态下的虚函数表内存
- 5.派生类函数中多出来的虚函数访问("基类指针指向子类对象")
1.Class的内存分布
#include <iostream>
using namespace std;
#define Debug cout<<'<'<<__FILE__<<">,"<<__FUNCTION__<<","<<__LINE__<<endl
class Base
{
};
int main()
{
cout<<sizeof(Base)<<endl;//1
return 0;
}
#include <iostream>
using namespace std;
#define Debug cout<<'<'<<__FILE__<<">,"<<__FUNCTION__<<","<<__LINE__<<endl
class Base
{
private:
int _val;
int static _staticval;//说明静态成员变量不属于类
public:
int Getval() const//说明成员函数也不属于类
{
return _val;
}
};
int main()
{
cout<<sizeof(Base)<<endl;//4
return 0;
}
#include <iostream>
using namespace std;
#define Debug cout << '<' << __FILE__ << ">," << __FUNCTION__ << "," << __LINE__ << endl
class Base
{
private:
int _val;
int static _staticval; // 说明静态成员变量不属于类
public:
int Getval() const // 说明成员函数也不属于类
{
return _val;
}
};
class Base_A : public Base
{
};
int main()
{
cout << sizeof(Base_A) << endl; // 4
printf("%p\n%p\n", &Base::Getval, &Base_A::Getval);
/*
00007FF6D2AC1131
00007FF6D2AC1131 //不重载的话,从派生类继承下来的函数是同一个函数,也就是说"函数的内存不在类里面"
*/
return 0;
}
#include <iostream>
using namespace std;
#define Debug cout<<'<'<<__FILE__<<">,"<<__FUNCTION__<<","<<__LINE__<<endl
class Base
{
private:
int _val;
int static _staticval;//说明静态成员变量不属于类
public:
int Getval() const//说明成员函数也不属于类
{
return _val;
}
};
class Base_A : public Base
{
public:
int Getval() const
{
return 0;
}
};
int main()
{
cout<<sizeof(Base_A)<<endl;//4
printf("%p\n%p\n",&Base::Getval,&Base_A::Getval);
/*
000000000061fdf0
000000000061fde0 //重载了所以地址才发生了变化
*/
return 0;
}
2.其他修改Class中变量的方法
类这种概念只在编译的时候存在
#include <iostream>
using namespace std;
#define Debug cout << '<' << __FILE__ << ">," << __FUNCTION__ << "," << __LINE__ << endl
class Base
{
private:
int _val;
int _valB;
public:
int GetVal() const
{
return _val;
}
int GetValB() const
{
return _valB;
}
};
int main()
{
Base obj;
int* objPtr = (int*)&obj;
*objPtr = 999;
*(objPtr + 1) = 1000;
cout << obj.GetVal() << endl;//999
cout << obj.GetValB() << endl;//1000
return 0;
}
#include <iostream>
using namespace std;
#define Debug cout << '<' << __FILE__ << ">," << __FUNCTION__ << "," << __LINE__ << endl
class Base
{
public:
virtual void VirA()
{
Debug;
}
virtual void VirB()
{
Debug;
}
virtual void VirC()
{
Debug;
}
};
using U8 = long long;
using FuncPtr = void(*)();//函数指针
int main()
{
Base obj;
U8* objAddre = (U8*)&obj;//这个类
U8* objArr = (U8*)(*objAddre);//解引用,这时候,就是那个虚函数的数组
((FuncPtr)objArr[0]) ();
((FuncPtr)objArr[1]) ();
((FuncPtr)objArr[2]) ();
/*输出
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirA,13
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirB,17
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirC,21
*/
return 0;
}
3.通过虚函数表内存偏移调用虚函数
#include <iostream>
using namespace std;
#define Debug cout << '<' << __FILE__ << ">," << __FUNCTION__ << "," << __LINE__ << endl
class Base
{
public:
virtual void VirA()
{
Debug;
}
virtual void VirB()
{
Debug;
}
virtual void VirC()
{
Debug;
}
};
using U8 = long long;
using FuncPtr = void(*)();//函数指针
int main()
{
Base obj;
U8* objAddre = (U8*)&obj;//这个类
U8* objArr = (U8*)(*objAddre);//解引用,这时候,就是那个虚函数的数组
((FuncPtr)objArr[0]) ();
((FuncPtr)objArr[1]) ();
((FuncPtr)objArr[2]) ();
/*输出
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirA,13
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirB,17
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirC,21
*/
return 0;
}
#include <iostream>
using namespace std;
#define Debug cout << '<' << __FILE__ << ">," << __FUNCTION__ << "," << __LINE__ << endl
class Base
{
public:
virtual void VirA()
{
Debug;
}
virtual void VirB()
{
Debug;
}
virtual void VirC()
{
Debug;
}
};
using U8 = long long;
using FuncPtr = void(*)();//函数指针
int main()
{
{
Base obj;
U8* objAddre = (U8*)&obj;//这个类
U8* objArr = (U8*)(*objAddre);//解引用,这时候,就是那个虚函数的数组
printf("%p\n", objArr);
((FuncPtr)objArr[0]) ();
((FuncPtr)objArr[1]) ();
((FuncPtr)objArr[2]) ();
}
{
Base obj;
U8* objAddre = (U8*)&obj;//这个类
U8* objArr = (U8*)(*objAddre);//解引用,这时候,就是那个虚函数的数组
printf("%p\n",objArr);
}
/*输出
00007FF63F7BBD30
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirA,13
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirB,17
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirC,21
00007FF63F7BBD30///地址一样:说明:同一个类 实例化多个对象 虚函数表指针是一样的
*/
return 0;
}
4.继承状态下的虚函数表内存
- 1.没有重写,虚函数表中的元素地址是一样的
- 2.重写后,对应的虚函数表中的函数指针就是新的地址
#include <iostream>
using namespace std;
#define Debug cout << '<' << __FILE__ << ">," << __FUNCTION__ << "," << __LINE__ << endl
class Base
{
public:
virtual void VirA()
{
Debug;
}
virtual void VirB()
{
Debug;
}
virtual void VirC()
{
Debug;
}
};
class A_Base : public Base
{
virtual void VirB()
{
Debug;
}
};
using U8 = long long;
using FuncPtr = void(*)();//函数指针
int main()
{
{
A_Base obj;
U8* objAddre = (U8*)&obj;//这个类
U8* objArr = (U8*)(*objAddre);//解引用,这时候,就是那个虚函数的数组
//printf("%p\n", objArr);
((FuncPtr)objArr[0]) ();
((FuncPtr)objArr[1]) ();
((FuncPtr)objArr[2]) ();
}
/*输出
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirA,13
<D:\vs2022\code\测试cpp语法\main.cpp>,A_Base::VirB,29
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirC,21
*/
return 0;
}
#include <iostream>
using namespace std;
#define Debug cout << '<' << __FILE__ << ">," << __FUNCTION__ << "," << __LINE__ << endl
class Base
{
public:
virtual void VirA()
{
Debug;
}
virtual void VirB()
{
Debug;
}
virtual void VirC()
{
Debug;
}
};
class A_Base : public Base
{
virtual void VirB()
{
Debug;
}
};
using U8 = long long;
using FuncPtr = void(*)();//函数指针
int main()
{
{
Base obj;
U8* objAddre = (U8*)&obj;//这个类
U8* objArr = (U8*)(*objAddre);//解引用,这时候,就是那个虚函数的数组
//printf("%p\n", objArr);
((FuncPtr)objArr[0]) ();
((FuncPtr)objArr[1]) ();
((FuncPtr)objArr[2]) ();
for(int i = 0 ; i < 3 ; i++)
printf("%p\n", objArr[i]);
}
printf(".........................................................\n");
{
A_Base obj;
U8* objAddre = (U8*)&obj;//这个类
U8* objArr = (U8*)(*objAddre);//解引用,这时候,就是那个虚函数的数组
//printf("%p\n", objArr);
((FuncPtr)objArr[0]) ();
((FuncPtr)objArr[1]) ();
((FuncPtr)objArr[2]) ();
for (int i = 0; i < 3; i++)
printf("%p\n", objArr[i]);
}
/*输出
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirA,13
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirB,17
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirC,21
00007FF7979B1442
00007FF7979B115E
00007FF7979B1474
.........................................................
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirA,13
<D:\vs2022\code\测试cpp语法\main.cpp>,A_Base::VirB,29
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirC,21
00007FF7979B1442
00007FF7979B14B0
00007FF7979B1474
*/
return 0;
}
5.派生类函数中多出来的虚函数访问(“基类指针指向子类对象”)
“基类指针指向子类对象"或"基类指针指向派生类对象”
#include <iostream>
using namespace std;
#define Debug cout << '<' << __FILE__ << ">," << __FUNCTION__ << "," << __LINE__ << endl
class Base
{
public:
virtual void VirA()
{
Debug;
}
virtual void VirB()
{
Debug;
}
virtual void VirC()
{
Debug;
}
};
class A_Base : public Base
{
virtual void VirB()
{
Debug;
}
virtual void VirD()
{
Debug;
}
};
using U8 = long long;
using FuncPtr = void(*)();//函数指针
int main()
{
{
Base obj;
U8* objAddre = (U8*)&obj;//这个类
U8* objArr = (U8*)(*objAddre);//解引用,这时候,就是那个虚函数的数组
//printf("%p\n", objArr);
((FuncPtr)objArr[0]) ();
((FuncPtr)objArr[1]) ();
((FuncPtr)objArr[2]) ();
for (int i = 0; i < 3; i++)
printf("%p\n", objArr[i]);
}
printf(".........................................................\n");
{
A_Base obj;
U8* objAddre = (U8*)&obj;//这个类
U8* objArr = (U8*)(*objAddre);//解引用,这时候,就是那个虚函数的数组
//printf("%p\n", objArr);
((FuncPtr)objArr[0]) ();
((FuncPtr)objArr[1]) ();
((FuncPtr)objArr[2]) ();
for (int i = 0; i < 3; i++)
printf("%p\n", objArr[i]);
}
{
Base* obj = new A_Base;
//obj->VirD();//报错:常规的方法:基类指针 无法访问 派生类函数中多出来的虚函数
U8* objAddre = (U8*)obj;//这个类
U8* objArr = (U8*)(*objAddre);//解引用,这时候,就是那个虚函数的数组
((FuncPtr)objArr[3]) ();
}
/*输出
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirA,13
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirB,17
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirC,21
00007FF71EBE14CE
00007FF71EBE1181
00007FF71EBE150A
.........................................................
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirA,13
<D:\vs2022\code\测试cpp语法\main.cpp>,A_Base::VirB,29
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirC,21
00007FF71EBE14CE
00007FF71EBE10A5
00007FF71EBE150A
<D:\vs2022\code\测试cpp语法\main.cpp>,A_Base::VirD,34
*/
return 0;
}