一个class的data member可以表现这个class在程序执行时的某种状态。Nonstatic data member放置的是“个别的class object”感兴趣的数据,static data member则放置的是“整个class”感兴趣的数据,其中不管class创建出多少个objects,static data member永远只存在一份实例,但是一个template class的static data members的行为稍有不同。
3.1 虚函数表位置分析
类:如果类中有虚函数的话,那么这个类就会生成虚函数表;
类对象:其有一个指针,这个指针(vptr)会指向这个虚函数表的开始地址;
// 验证虚函数表的位置
class A {
public:
int i;
virtual void testfunc() {}; //虚函数
};
int main() {
A aobj; //创建一个A的对象
int ilen = sizeof(aobj); //ilen表示对象aobj的字节大小
char* p1 = reinterpret_cast<char*>(&aobj);
char* p2 = reinterpret_cast<char*>(&(aobj.i));
if (p1 == p2) { //如果相同,那么说明aobj.i的地址和aobj相同
cout << "i在对象aobj内存布局的上边" << endl;
} else {
cout << "vptr在对象aobj内存布局的上边" << endl;
}
return 0;
}
打印结果为虚函数表指针在对象内存的开头
3.2 继承关系作用下虚函数的手工调用
class Base {
public:
virtual void f() { cout << "Base::f()" << endl; }
virtual void g() { cout << "Base::g()" << endl; }
virtual void h() { cout << "Base::h()" << endl; }
};
class Derive : public Base {
public:
virtual void g() { cout << "Derive::g()" << endl; }
};
int main() {
//继承关系作用下虚函数的手工调用
cout << sizeof(Base) << endl;
cout << sizeof(Derive) << endl;
Derive *d = new Derive(); //派生类指针
long *pvptr = (long*)d; //指向对象的指针d转成了long*类型
long *vptr = (long*)(*pvptr); //(*pvptr)表示pvptr指向的对象,也就是Derive本身,也代表着虚函数表指针地址;
//打印虚函数地址,实际是三个虚函数
for (int i = 0; i<3; ++i) {
printf("vptr[%d] = 0x:%p\n", i, vptr[i]);
}
typedef void(*Func)(void); //定义一个函数指针类型
Func f= (Func)vptr[0]; //f就是函数指针变量,vptr[0]是指向第一个虚函数的
Func g= (Func)vptr[1];
Func h= (Func)vptr[2];
f();
g();
h();
//打印父类的虚函数依次类推
return 0;
}