赞
踩
①如果一个类有虚函数,那么这个类会产生一个虚函数表
②有虚函数表的类,对象里面有一个指针(虚函数表指针)用来指向这个虚函数表的起始地址(这个指针一般占用4字节或8字节)
③这个虚函数表的指针可能位于对象内存的开头
或对象内存的末尾
,具体位置实现取决于编译器的实现
,下面代码的实现说明了可能在对象的头
#include <iostream>
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 <iostream> 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 <iostream> 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,也依旧是虚函数
③父类有张虚函数表,子类有另一张表,只不过两者不被子类覆盖的部分,在两者的虚函数表中两者的内容相同
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。