①如果一个类有虚函数,那么这个类会产生一个虚函数表
②有虚函数表的类,对象里面有一个指针(虚函数表指针)用来指向这个虚函数表的起始地址(这个指针一般占用4字节或8字节)
③这个虚函数表的指针可能位于对象内存的开头
或对象内存的末尾
,具体位置实现取决于编译器的实现
,下面代码的实现说明了可能在对象的头
#include
class A
{
public:
int64_t i; //八字节
virtual void testfunc(){} //虚函数
};
int main()
{
A a;
int iLen = sizeof(a);
printf("A a size:%d\n",iLen);
return 0;
}
#include
class A
{
public:
int64_t i; //
virtual void testfunc(){} //虚函数
};
int main()
{
A a;
char* p1 = reinterpret_cast<char*>(&a); //强转
char* p2 = reinterpret_cast<char*>(&(a.i));//
//若整数地址和对象地址一样,说明虚函数表指针存在对象末尾
if(p1 == p2)
{
printf("虚函数表指针存在对象末尾 \n");
}
//虚函数表指针存在对象头
else
{
printf("虚函数表指针存在对象头 \n");
}
return 0;
}
①父类对象调用的虚函数都是父类的
②子类的虚函数把父类的虚函数覆盖
的话,这个虚函数对应的地址是不一样
的;其他不被子类覆盖
的虚函数地址值是相同的
(下面代码体现)
#include
class Base
{
public:
virtual void f(){std::cout<<"Base::f()"<<std::endl;}
virtual void g(){std::cout<<"Base::g()"<<std::endl;}
virtual void h(){std::cout<<"Base::h()"<<std::endl;}
};
class Derive:public Base
{
public:
void g() override
{std::cout<<"Derive::g()"<<std::endl;}
};
typedef void(* Func)(void); //定义返回类型是void,参数是void的函数指针,叫Func
int main()
{
std::cout<<sizeof(Base)<<std::endl;
std::cout<<sizeof(Derive)<<std::endl;
Derive* d = new Derive(); //派生类指针,这里用基类指针也一样,Base* d = new Derive()
long* pvptr = (long*)d; //指向对象的指针换成(long*)类型的,目前对象d只有虚函数表指针
long* vptr = (long*)(*pvptr); //(*pvptr)表示pvptr指向的派生类对象,这个对象8字节
//vptr代表Derive对象虚函数表指针,指向派生类的虚函数表
//打印派生类虚函数的地址
for(int i = 0;i <=4;++i)
{
printf("vptr[%d] = 0x:%p\n",i,vptr[i]); //前三个是虚函数入口地址
}
Func f = (Func)vptr[0];
Func g = (Func)vptr[1];
Func h = (Func)vptr[2];
Func i = (Func)vptr[3];
Func j = (Func)vptr[4];
//打印派生类函数
f();//
g();//
h();//
return 0;
}
[lighthouse@VM-4-14-centos test_random]$ g++ *.h *.cpp
[lighthouse@VM-4-14-centos test_random]$ ./a.out
8
8
vptr[0] = 0x:0x400b50 //虚函数f()的地址
vptr[1] = 0x:0x400bd4 //虚函数表里面派生类函数g()的地址,这里被派生类覆盖了
vptr[2] = 0x:0x400ba8 //虚函数h()的地址
vptr[3] = 0x:(nil)
vptr[4] = 0x:0x400d88
Base::f()
Derive::g()
Base::h()
[lighthouse@VM-4-14-centos test_random]$
int main()
{
std::cout<<sizeof(Base)<<std::endl;
std::cout<<sizeof(Derive)<<std::endl;
Derive* d = new Derive(); //派生类指针,这里用基类指针也一样,Base* d = new Derive()
long* pvptr = (long*)d; //指向对象的指针换成(long*)类型的,目前对象d只有虚函数表指针
long* vptr = (long*)(*pvptr); //(*pvptr)表示pvptr指向的派生类对象,这个对象8字节
//vptr代表Derive对象虚函数表指针,指向派生类的虚函数表
Base* b = new Base();
long* pvptr_b = (long*)b;
long* vptr_b = (long*)(*pvptr_b);
//打印派生类虚函数的地址
for(int i = 0;i <=4;++i)
{
printf("vptr[%d] = 0x:%p\n",i,vptr[i]); //前三个是虚函数入口地址
}
Func f = (Func)vptr[0];
Func g = (Func)vptr[1];
Func h = (Func)vptr[2];
//打印派生类
f();//
g();//
h();//
//
printf("Base:\n");
for(int i = 0;i <=4;++i)
{
printf("vptr[%d] = 0x:%p\n",i,vptr_b[i]); //前三个是虚函数入口地址
}
Func f_b = (Func)vptr_b[0];
Func g_b = (Func)vptr_b[1];
Func h_b = (Func)vptr_b[2];
//打印派生类
f_b();//
g_b();//
h_b();//
return 0;
}
[lighthouse@VM-4-14-centos test_random]$ g++ *.h *.cpp
[lighthouse@VM-4-14-centos test_random]$ ./a.out
8
8
vptr[0] = 0x:0x400c28
vptr[1] = 0x:0x400cac
vptr[2] = 0x:0x400c80
vptr[3] = 0x:(nil)
vptr[4] = 0x:0x400e70
Base::f()
Derive::g()
Base::h()
Base:
vptr[0] = 0x:0x400c28 //没被派生类覆盖的地址是相同的
vptr[1] = 0x:0x400c54 //被派生类虚函数覆盖的函数地址,基类指向自己的虚函数
vptr[2] = 0x:0x400c80
vptr[3] = 0x:0x601d98
vptr[4] = 0x:0x400e68
Base::f()
Base::g()
Base::h()
①同一类不同对象的虚函数表指针指向同一张虚函数表
②只要在父类中是虚函数,子类不用标virtual,也依旧是虚函数
③父类有张虚函数表,子类有另一张表,只不过两者不被子类覆盖的部分,在两者的虚函数表中两者的内容相同