一.单个类带虚函数的成员布局
class A
{
public:
int m_i;
int m_j;
virtual void func() {}
};
int main()
{
cout<<sizeof(A)<<endl; //12
printf("m_i的偏移量=%d\n",&A::m_i); //4
printf("m_j的偏移量=%d\n",&A::m_j);//8
}
图示说明:
![](https://i-blog.csdnimg.cn/blog_migrate/ec2a6e3086af7dc01944fe46162b078a.png)
因为类里面存在虚函数,所以会产生一个虚函数表,存在于类所有成员的上面;
二.单一继承 父类带虚函数 的成员布局
class Base1
{
public:
int b_i;
virtual void func() {}
};
class A:public Base1
{
public:
int m_i;
int m_j;
virtual void func() {}
};
int main()
{
cout << sizeof(Base1) << endl; //8
cout << sizeof(A) << endl; //16
printf("b_i的偏移量=%d\n", &A::b_i);//4
printf("m_i的偏移量=%d\n", &A::m_i); //8
printf("m_j的偏移量=%d\n", &A::m_j); //12
return 0;
}
图示理解:
![](https://i-blog.csdnimg.cn/blog_migrate/4e3e54d68709237b6dabc8a8306bcb0b.png)
基类和派生类都是带有虚函数的,但是最后派生类只有一个vptr(虚函数表指针),说明基类和派生类公用一个虚函数表指针。
三.单一继承 父类不带虚函数 的数据成员布局
class Base1
{
public:
int b_i;
};
class A:public Base1
{
public:
int m_i;
int m_j;
virtual void func() {}
};
int main()
{
cout << sizeof(Base1) << endl; //4
cout << sizeof(A) << endl; //16
printf("b_i的偏移量=%d\n", &A::b_i);//0
printf("m_i的偏移量=%d\n", &A::m_i); //8
printf("m_j的偏移量=%d\n", &A::m_j); //12
return 0;
}
看到b_i的偏移量是0的时候,可能会以为内存布局是这样的:
![](https://i-blog.csdnimg.cn/blog_migrate/0b89052678b4cf8f7aefe8666c650d95.png)
其实,并不是这样的,偏移量为0是b_i对Base1的偏移量是0,并不是对A类的偏移量是0,真实的内存布局应该是这样的:
![](https://i-blog.csdnimg.cn/blog_migrate/3ad7b9c4c60af12db98823c00bd4685f.png)
四.单一继承数据成员布局 this指针偏移知识补充
1.Base1中没有虚函数
class Base1
{
public:
int b_i;
Base1()
{
printf("Base1的this指针是%p\n",this);
};
class A:public Base1
{
public:
int m_i;
int m_j;
virtual void func() {}
Base1()
{
printf("A的this指针是%p\n",this);
}
};
int main()
{
cout << sizeof(Base1) << endl; //4
cout << sizeof(A) << endl; //16
printf("b_i的偏移量=%d\n", &A::b_i);//0
printf("m_i的偏移量=%d\n", &A::m_i); //8
printf("m_j的偏移量=%d\n", &A::m_j); //12
return 0;
}
图示理解:
![](https://i-blog.csdnimg.cn/blog_migrate/bd17918f643b7da7b1ed7e31db163ac2.png)
2.多重继承且父类都带虚函数的数据成员布局
class Base1
{
public:
int b_i;
Base1()
{
printf("Base1的this指针是:%p\n", this);
}
virtual void func() {}
};
class Base2
{
public:
int b2_i;
Base2()
{
printf("Base2的this指针是:%p\n", this);
}
virtual void func2() {}
};
class A:public Base1,public Base2
{
public:
int m_i;
int m_j;
virtual void func() {}
A()
{
printf("A的this指针是:%p\n", this);
}
~A(){}
};
int main()
{
cout << sizeof(Base1) << endl; //8
cout << sizeof(Base2) << endl; //8
cout << sizeof(A) << endl; //24
printf("b_i的偏移量=%d\n", &A::b_i);//4
printf("b2_i的偏移量=%d\n", &A::b2_i);//4
printf("m_i的偏移量=%d\n", &A::m_i); //16
printf("m_j的偏移量=%d\n", &A::m_j); //20
![](https://i-blog.csdnimg.cn/blog_migrate/21f74199210afd24a7dde462cc5442ee.png)
图示理解:
![](https://i-blog.csdnimg.cn/blog_migrate/f6d8932ca87153a04f8fa881e72af1ca.png)
结论:
我们要访问一个类对象中的成员,成员的定位是通过this指针,以及该成员的偏移值来确定的;
比如要访问b_i;this指针就会调整为Base1的this指针,然后再偏移4就得到了b_i的位置;
五.父类直至指向子类的多态分析:
int main()
{
A a;
a.b_i = 1;
a.m_i = 2;
a.m_j = 3;
Base1* p1 = &a;
Base2* p2 = &a;
// a的地址: a = 0x0043fc0c
// p1的地址: p1 = 0x0043fc0c // 没有进行调整;
// p2的地址: p2 = 0x0043fc14 // 进行了调整,指向了Base2所指向的空间;
Base2* pa = new A(); // pa = 0x00f0e2c0
A* pb= (A*)pa; // pb = 0x00f0e2b8
//delete pa; // 这里会报错; 因为pa里面存储的不是当时new的所有内容;
delete pb; //得这样释放;
return 0;
}
六.更复杂的继承
![](https://i-blog.csdnimg.cn/blog_migrate/ff62baabfdd4274eaa7d33c6b00f276a.png)
我的内容可能写的比较潦草,如果需要笔记内容相对应的课程,请关注并私信我。