C++对象模型那点事儿(布局篇)
1 前言
上篇我们尽量说一些宏观上的东西,数据成员与函数成员在类中的布局将在微观篇中具体讨论。
假设你对这些东西也感兴趣,那么好,我们一起将class的底层翻个底朝天。修炼好底层的内功,我想对于上层的提供。帮助可不止一点点吧?
2 class总体布局
typedef struct point3d{ //数据
float x;
float y;
float z;
}Point3d;
void Point3d_print(const Point3d *pd{
printf("%g,%g,%g",pd->x,pd->y,pd->z);
}
我们再来看看C++中的做法。
class Point3d{
float _x;
float _y;
float _z;
public:
void point3d_print(){
printf("%g,%g,%g",_x,_y,_z);
}
};
在Point3d转换到C++之后。我们可能会问加上封装之后,成本会添加多少?
那么以下我们举样例来证明上述的讨论。
class A{}; // sizeof(A) = 1 有木有非常奇怪?稍后说明
class B{int x;}; // sizeof(B) = 4
class C{
int x;
public:
int get(){return x;}
}; // sizeof(C) = 4; 是不是验证了我们上述的论述?
这样做使得用同一个空类定义两个对象的时候得以在内存中配置独一无二的地址。
A a,b;
if(&a == &b)cout<<"error"<<endl;
我们都知道在C语言中struct优化的时候会进行内存对齐,那么我们来看看class中有没有这个优化。
class A{
char x;
int y;
char z;
}; // sizeof(A) == 12;
class B{
char x;
char y;
int z;
}; // sizeof(B) = 8;
class C{
int x;
char y;
char z;
}; // sizeof(C) = 8;
class D{
long long x;
char y;
char z;
}; //sizeof(D) = 16; 因为longlong为8字节大小,此处以8字节对齐
显然编译器进行类内存对齐的优化。
static和nonstatic function members 则被放在对象之外。
class A{
static int x;
}; //sizeof(A) = 1;
class B{
int x;
public:
int get(){
return x;
}
}; //sizeof(B) = 4
class C{
int x;
public:
virtual int get(){
return x;
}
}; //sizeof(C) = 8;
显然验证了上述我所说的。
class A{
void (*pf)(); //函数指针
}; //sizeof(A) = 4;
class B{
int *p; // 指针
}; //sizeof(B) = 4;
3 虚函数
class X{
int a;
int b;
public:
virtual void foo1(){cout<<"X::foo1"<<endl;}
virtual void foo2(){cout<<"X::foo2"<<endl;}
};
内存布局例如以下:
#include<iostream>
using namespace std;
class X{
int _a;
int _b;
public:
virtual void foo1(){cout<<"X::foo1"<<endl;}
virtual void foo2(){cout<<"X::foo2"<<endl;}
};
typedef void (*pf)();
int main(){
X a;
int **tmp = (int **)&a;
pf ptf;
for(int i=0;i<2;i++){
ptf = (pf)tmp[0][i];
ptf();
}
}
执行结果例如以下图所看到的:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYmxvb2RfZmxvd2luZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />
4 继承
class A{
int x;
};
class B:public A{
int y;
}; //sizeof(B) = 8;
我们来看看涉及到继承的时候内存的布局情况。
class C{
public:
virtual void fooC(){
cout<<"C::fooC()"<<endl;
}
}; //sizeof(C) = 4;
class D:public C{
int a;
public:
virtual void fooD(){
cout<<"D::fooD()"<<endl;
}
}; //sizeof(D) = 8;
内存布局应该是这个样子:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYmxvb2RfZmxvd2luZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />
typedef void (*pf)();
int main(){
C a;
D b;
int **tmpc = (int **)&a;
int **tmpb = (int **)&b;
pf ptf;
ptf = (pf)tmpc[0][0];
ptf();
ptf = (pf)tmpb[0][0];
ptf();
ptf = (pf)tmpb[0][1];
ptf();
}
执行结果:
我们再来看看以下的涉及到虚函数的多重继承。
class A{
int _a;
public:
virtual void fooA(){
cout<<"C::fooA()"<<endl;
}
virtual void poo(){
cout<<"A::poo()"<<endl;
}
}; //sizeof(A) = 8;
class B{
int _b;
public:
virtual void fooB(){
cout<<"C::fooB()"<<endl;
}
virtual void poo(){
cout<<"B::poo()"<<endl;
}
}; ////sizeof(B) = 8;
class C:public A,public B{
int _c;
public:
void poo(){
cout<<"C::poo()"<<endl;
}
virtual void hoo(){
cout<<"C::hoo()"<<endl;
}
}; //sizeof(C) = 20;
typedef void (*pf)();
int main(){
C a;
int **tmp = (int **)&a;
pf ptf;
for(int i=0;i<3;++i){
ptf = (pf)tmp[0][i];
ptf();
}
cout<<"-----------"<<endl;
int s = sizeof(A)/4; //指针与int都占用4字节大小
for(int i=0;i<2;i++){
ptf = (pf)tmp[2][i];
ptf();
}
}
执行结果:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYmxvb2RfZmxvd2luZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />
class A{
int _a1;
int _a2;
}; //sizeof(A) = 8;
class B:virtual public A{
int b;
}; //sizeof(B) = 16;
class C:virtual public A{
int c;
}; //sizeof(C) = 16;
class D:public B,public C{
int d;
}; //sizeof(D) = 28;
我们来看看这时候的内存布局:
int main(){
D d;
A *pta = &d;
B *ptb = &d;
C *ptc = &d;
cout<<"D: "<<&d<<endl;
cout<<"B: "<<ptb<<" C: "<<ptc<<endl;
cout<<"A: "<<pta<<endl;
}
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYmxvb2RfZmxvd2luZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />
C++对象模型那点事儿(布局篇)的更多相关文章
- C++对象模型的那些事儿之三:默认构造函数
前言 继前两篇总结了C++对象模型及其内存布局后,我们继续来探索一下C++对象的默认构造函数.对于C++的初学者来说,有如下两个误解: 任何class如果没有定义default constructor ...
- C++对象模型的那些事儿之二:对象模型(下)
前言 上一篇博客C++对象模型的那些事儿之一为大家讲解了C++对象模型的一些基本知识,可是C++的继承,多态这些特性如何体现在对象模型上呢?单继承.多重继承和虚继承后内存布局上又有哪些变化呢?多态真正 ...
- C++对象模型的那些事儿之五:NRV优化和初始化列表
前言 在C++对象模型的那些事儿之四:拷贝构造函数中提到如果将一个对象作为函数参数或者返回值的时候,会调用拷贝构造函数,编译器是如何处理这些步骤,又会对其做哪些优化呢?本篇博客就为他家介绍一个编译器的 ...
- C++对象模型的那些事儿之四:拷贝构造函数
前言 对于一个没有实例化的空类,编译器不会给它默认生成任何函数,当实例化一个空类后,编译器会根据需要生成相应的函数.这类函数包括一下几个: 构造函数 拷贝构造函数 析构函数 赋值运算符 在上一篇博文C ...
- 【转载】图说C++对象模型:对象内存布局详解
原文: 图说C++对象模型:对象内存布局详解 正文 回到顶部 0.前言 文章较长,而且内容相对来说比较枯燥,希望对C++对象的内存布局.虚表指针.虚基类指针等有深入了解的朋友可以慢慢看.本文的结论都在 ...
- react-native 之布局篇
一.宽度单位和像素密度 react的宽度不支持百分比,设置宽度时不需要带单位,那么默认的单位是什么呢? /** * Sample React Native App * https://github.c ...
- 深入css布局篇(3)完结 — margin问题与格式化上下文
深入css布局(3) - margin问题与格式化上下文 在css知识体系中,除了css选择器,样式属性等基础知识外,css布局相关的知识才是css比较核心和重要的点.今天我们来深入学习一下 ...
- 深入css布局篇(2) — 定位与浮动
深入css布局(2) - 定位与浮动 在css知识体系中,除了css选择器,样式属性等基础知识外,css布局相关的知识才是css比较核心和重要的点.今天我们来深入学习一下css布局相关的知识 ...
- Angular Material 教程之布局篇
Angular Material 教程之布局篇 (一) : 布局简介https://segmentfault.com/a/1190000007215707 Angular Material 教程之布局 ...
- 【WPF】 布局篇
[WPF] 布局篇 一. 几个常用且至关重要的属性 1. Width,Height : 设置窗体,控件宽高. 这里注意,WPF是自适应的, 所以把这2个属性设置 Auto, 则控件宽高会自动改变. 2 ...
随机推荐
- PHP文件包含
今天突然发现这个东西有好多奇怪的东西,特别写一下记一下 测试用的1.txt及phpinfo.php内容都是phpinfo() 截断: 好多.和好多./截断:这里不测试了,摘自代码审计一书,5.2可用, ...
- Retrofit--官网2.1.0
Retrofit--官网2.1.0 android Retrofit 介绍 API 描述 请求方法 URL 处理 请求体 表单的 ENCODED 和 MULTIPART HEADER 处理 同步 VS ...
- highCharts+Struts2生成柱状图
这篇文章主要结合Struts2+json+Highcharts实现动态数据的显示.为了节省时间,就不写数据库了.在action中用一个集合来模拟从数据库取到的数据.模拟数据为三个学生在不同时间成绩的变 ...
- 数码相机常用CCD/CMOS尺寸对比
数码相机的关键元件CCD或CMOS又称为“影像传感器”,其作用相当于感光胶片.CCD尺寸越大,采集光线的效果越好,画面记录的信息就越多,保留的细节也就越丰富,所以图像更完美漂亮. CCD尺寸的大小与像 ...
- Python+selenium之读取配置文件内容
Python+selenium之读取配置文件内容 Python支持很多配置文件的读写,此例子中介绍一种配置文件的读取数据,叫ini文件,python中有一个类ConfigParser支持读ini文件. ...
- Innodb间隙锁,细节讲解(转)
关于innodb间隙锁,网上有很多资料,在此不做赘述,我们讲解一下关于innodb的间隙锁什么情况下会产生的问题. 网上有些资料说innodb的间隙锁是为了防止幻读,这个论点真的是误人子弟.了解inn ...
- Duilib教程-简单介绍
在读这篇博客的时候,可能您已经对duilib有一定的了解.所以,我并不打算对duilib进行过多的介绍.它的内核首先由外国人编写,后来由国人一个小组接过来继续编写,于是就有了现在的Duilib. 1. ...
- 自己搭建v,p,n过程
安装dockeryum install -y docker把docker设置为服务systemctl enable docker.service启动dockersystemctl start dock ...
- linux ftp 上传与下载命令解析
month=`date -d "last month" +"%Y%m"` year=`date +"%Y"` rm /home/yourDi ...
- Visual Assist X助手的一些使用技巧和快捷键
部分快捷键 Shift+Alt+F // Find References 查找引用 Shift+Alt+S // FindSynbolDialog打开查找符号对话框 Alt+G // GotoImpl ...