通信人家园
标题:
[原创连载]大话C++对象模型(8月3日更新)
[查看完整版帖子]
[打印本页]
时间:
2011-8-2 23:31
作者:
calitrean
标题:
[原创连载]大话C++对象模型(8月3日更新)
一.从struct开始说起
相信每一个学过C语言的同学都知道struct,用于定义用户自己的抽象数据类型
OK,下面来看一段代码:
#include <iostream.h>
struct A
{
int a;
int b;
};
A a;
int main()
{
cout<<sizeof(a)<<"\n";
return 0;
}
复制代码
这是一段很普通的代码,定义了一个struct A类型的变量,然后将这个变量所占空间的大小给打印出来
细心的读者可能发现了这里的语法和以前学的纯C语言有点不太一样,在很多纯C编译器中,是不能这样定义的,要么在定义struct A类型的同时定义一个该类型的变量:
struct A
{
int a;
int b;
}a;
复制代码
要么先把struct A定义为一个类型,再定义一个该类型的变量:
typedef struct A
{
int a;
int b;
};
A a;
复制代码
为什么C++里面就可以这样定义呢?因为C++编译器对C进行了扩展
下面来看看C++特有的东西:class
class B
{
int a;
int b;
};
B b;
int main()
{
cout<<sizeof(b)<<"\n";
return 0;
}
复制代码
和前面一样,我们定义了class B和它的对象b,并且打印了这个对象所占空间的大小。
运行这两个程序,发现它们的运行结果是相同的:
呵呵,是不是感觉struct和class有些相似啊!但是您可能会问“class里可以定义函数,struct里也可以吗?”,答案当然是肯定的。
现在我们将代码修改一下:
#include <iostream.h>
struct A
{
int a;
int b;
void f()
{
}
};
A a;
class B
{
int a;
int b;
void f()
{
}
};
B b;
int main()
{
cout<<"\n"<<sizeof(a)<<"\n";
cout<<"\n"<<sizeof(b)<<"\n";
return 0;
}
复制代码
运行结果如下:
可以看出,struct里面也是可以定义函数的。那么,struct和class真的完全一样吗?答案是否定的
我们再将代码修改一下:
#include <iostream.h>
struct A
{
int a;
int b;
void f()
{
}
};
A a;
class B
{
int a;
int b;
void f()
{
}
};
B b;
int main()
{
a.a = 1;
b.a = 2;
return 0;
}
复制代码
运行代码,结果。。。无法通过编译,编译器报错:
原来出错点在这里:
看来struct和class还是有不同的!不同点就在于:
struct成员的默认访问权限是
public
的,而class成员的默认访问权限是
private
的!
可见,struct和class并没有本质区别,除了默认的成员访问权限不一样。
也许您学过JAVA,会发现在C++中定义class和在JAVA里不大一样,在C++里定义一个class后需要一个“;”,但是在JAVA中缺不需要。
这是因为C++是从C演化而来,这个“++”就是在C的基础上加了一些东西,一些面向对象的东西。同理,GPRS可以叫GSM++,HSPA可以叫3G++,呵呵
回到正题,因为C++是从C语言演化而来,是C语言的超集,因此保留了向前兼容的特性,因为C++中的class是从C语言中的struct演化而来,因此保留了struct定义时的格式:
struct A{};
class B{};
复制代码
在这点上,JAVA似乎要洒脱得多:
public class A{}
复制代码
可以看出,在面向对象方面,JAVA比C++更彻底,因为JAVA自诞生之日起就是一门纯粹的面向对象的编程语言。
[
本帖最后由 家园小助手 于 2011-12-20 14:30 编辑
]
时间:
2011-8-3 01:44
作者:
calitrean
二.成员对齐方式
来看看我们之前定义的struct:
struct A
{
int a;
int b;
};
复制代码
假设我们用的是32位CPU,那么sizeof(A)的结果是什么?
嗨,那还不简单嘛,32位机器上int类型占4个字节,struct A里面有2个int型变量,那就是8个字节呗!
没错,在这个例子中,sizeof(A)的结果的确是8,上一节中的运行结果证明了这个结论。
下面,我们把代码修改一下:
struct A
{
int a;
short b;
};
int main()
{
cout<<sizeof(short)<<"\n";
cout<<sizeof(A)<<"\n";
return 0;
}
复制代码
运行结果如下:
呵呵,是不是感觉有点不对啊,int a占4个字节,short b占2个字节,struct A应该是4+2=6个字节啊,怎么变成8个了???
我们还是从CPU的内存地址对齐开始说起吧。。。
以32位CPU为例,在自然对齐方式下,基本数据类型(如short,int,double)变量的地址必须被他们的大小整除。这句话怎么理解呢?打个比方,对于int类型的变量,因为sizeof(int)等于4,因此存放int类型变量的起始地址必须能被4整除。同理,short类型变量的起始地址要能被2整除,对于char和bool类型的变量则没有特别要求。
回到我们的程序中,在struct A中,int a的起始地址应该能被4整除,short b的起始地址应该能被2整除。我们来看看下面这段程序的运行结果:
struct A
{
int a;
short b;
};
A a;
int main()
{
cout<<"\n"<<&a.a<<"\n";
cout<<"\n"<<&a.b<<"\n";
return 0;
}
复制代码
可见,struct A中,int a的起始地址是0x0042E058,可以被4整除;short b的起始地址是0x0042E05C,可以被2整除。
但是这里有个问题,为什么sizeof(A)的值是8呢?这说明编译器在short b之后又自动填充了2个字节,因此sizeof(A)=sizeof(int a)+sizeof(short b)+2个填充字节。
编译器为什么要在short b的后面自动填充2个字节呢?当然是为了地址对齐。可是在这个例子中,即使不填充这2个字节,int a和short b的起始地址不也满足了CPU的对齐要求了吗?
是的,在单个struct A变量中,short b后面即使没有填充2个字节一样能满足CPU的地址对齐要求,但是如果我们定义一个struct A的数组呢,情况会怎样?
情景一:假设编译器没有在short b后面填充2个字节
我们定义一个struct A的数组:
struct A
{
int a;
short b;
};
A a[2];
复制代码
对应的内存地址如下:
可以看出,数组的第一个元素中的int a和short b都满足CPU的地址对齐要求,但是第二个元素中的int a显然不能满足CPU的地址对齐要求,因为0x0042E05E这个地址显然无法被4整除
情景二:假设编译器自动在short b后面填充2个字节
可以看出,第二个元素中的int a的地址是0x0042E060,显然该地址是能被4整除的
Oh,编译器自动填充那2个字节的目的就在于此!
这里有一个问题,那就是CPU为什么需要地址对齐呢?
在32位CPU中,数据总线是32位,一次可以存取4个字节的数据
现在,让我们重温一下前面的两个情景
情景一:假设编译器没有在short b后面填充2个字节
可以看出,对于A a[2]中第一个元素中的int a,CPU可以很方便地将其取出;对于之后的short b,CPU可以一次取出4个字节后丢弃高位的2个字节
但是对于A a[2]中第二个元素中的int a,CPU无法一次取出,须先去一次丢弃低位的2个字节再取一次丢弃高位的2个字节,然后将结果拼凑在一起
情景二:假设编译器自动在short b后面填充2个字节
可以看出,CPU只需一次就可以很方便地取出第二个元素中的int a
这就是以空间换取时间啊!
那么有没有办法能让CPU按照我自己的要求进行地址对齐呢?当然可以,利用#pragma pack宏就可以
比如,我想让CPU按照2个字节的方式对齐,则代码如下:
#include <iostream.h>
#pragma pack(push,2)
struct A{
int a;
short b;
};
A a;
int main()
{
cout<<"\n"<<sizeof(a)<<"\n";
cout<<"\n"<<&a.a<<"\n";
cout<<"\n"<<&a.b<<"\n";
return 0;
}
复制代码
运行结果如下:
可以看出,在我们要求的2字节对齐方式下,sizeof(a)的结果是6,struct A只占了6个字节,一点也没有浪费
[
本帖最后由 calitrean 于 2011-8-3 02:16 编辑
]
时间:
2011-8-3 02:33
作者:
calitrean
三.没有成员变量的struct
来看看下面这段代码:
#include <iostream.h>
struct A
{
};
A a;
int main()
{
cout<<"\n"<<sizeof(a)<<"\n";
return 0;
}
复制代码
运行结果如下:
似乎是有点不可思议,一个没有任何元素的struct居然占了1个字节的空间,这是为什么呢?
因为编译器自动的给它填充了一个字节
那么,为什么要填充这一个字节呢?
因为我们需要在内存中能找到变量A a。
如果一个变量不占任何空间,那它岂不是相当于不存在嘛!那不是变量,那是幽灵,至于您信不信,反正我是信了
所以,找一个字节必须要填充,那是A a存在的证据!
好了,我们再来看看另一端代码:
#include <iostream.h>
struct A
{
int a[0];
};
int main()
{
cout<<"\n"<<sizeof(A)<<"\n";
return 0;
}
复制代码
运行结果如下:
这似乎也是一个空的struct,运行的结果不出意料。
但是如果struct中还有其他变量呢?比如:
#include <iostream.h>
struct A
{
int a;
int b[0];
};
int main()
{
cout<<"\n"<<sizeof(A)<<"\n";
return 0;
}
复制代码
运行结果如下:
好像这次编译器没有在里面填充一个字节了,因为struct A里面有一个int a,因此struct A至少占了4个字节的空间,不会以幽灵的方式存在了,因此编译器就没有必要再往里面填充字节了。
但是,在struct A中定义一个int a[0]有意义吗?当然有
比如,某个家庭里有一个爸爸一个妈妈还有很多个孩子:
struct Family
{
int father;
int mother;
int children[0];
};
复制代码
某个周末,这对父母要带孩子们出去玩,他们可以带一个孩子,也可以带2个孩子,还可以带N个孩子:
int main()
{
Family *p=(Family*)malloc(sizeof(Family)+sizeof(int)*N);
p->children[0] = LUCY;
p->children[1] = TOM;
...
return 0;
}
复制代码
有一点要注意,0元素数组在struct和class之外定义
编译器会报错:
因为编译器认为之所以使用0元素数组是为了对struct和class作一个长度不可预知的扩展。
[
本帖最后由 calitrean 于 2011-8-3 03:20 编辑
]
时间:
2011-8-3 08:29
作者:
zxh315
ding
时间:
2011-8-3 08:38
作者:
ctao
c难学吗?想问问
时间:
2011-8-3 10:23
作者:
rillhu
写的很好啊,对于学C,又想学C++的很有指导意义。
时间:
2011-8-3 10:40
作者:
pxl888
标题:
回复 2# 的帖子
非常好啊,希望LZ能连载啊!
时间:
2011-8-3 11:51
作者:
猪没本事
貌似又是个连载,占楼学习,这种帖得留名
时间:
2011-8-3 12:06
作者:
愤怒的小麻雀
呵呵,学习一下。喜欢这种类型的
时间:
2011-8-3 12:36
作者:
北回归线以北
好东西 可以看看
时间:
2011-8-3 13:20
作者:
iamcatcher
呵呵 支持。不过这个论坛技术氛围不浓。
时间:
2011-8-3 13:58
作者:
klarke
不错,以资鼓励
时间:
2011-8-3 13:59
作者:
klarke
发帖随机事件
熬夜2周,软件调测工程被评优,省移动奖励 200 金钱,公司老总奖励 200 金钱,回家,为讨女友欢心,奖金全部上缴之外,还花去 2 金钱买礼物!
时间:
2011-8-3 14:21
作者:
923450
还没学过C++,学习一下
时间:
2011-8-3 16:47
作者:
bellinwater
终于看到楼主开贴了
时间:
2011-8-3 20:28
作者:
yonka
有意思~
东西比较基础~但是很耐人寻味~
时间:
2011-8-3 22:15
作者:
xiaoshan429
赶上直播了?留名。楼主加油
时间:
2011-8-4 08:47
作者:
13527598856
试试
时间:
2011-8-4 13:16
作者:
tjhwa
透彻
时间:
2011-8-4 14:13
作者:
mingming1105
mark
时间:
2011-8-4 20:17
作者:
irg723
看了此贴,一些原来模糊的概念清晰了,希望继续下去!
时间:
2011-8-6 13:19
作者:
xueshanfeiyign
风姐姐该给我们讲讲微电子。
时间:
2011-8-7 15:18
作者:
tbq2007
谢谢楼主,好人一生平安。
时间:
2011-8-7 21:58
作者:
AmazonBoy
楼主也应该加个#include <malloc.h>吧。。。
时间:
2011-8-7 23:25
作者:
loey
赶上直播帖了,对于只学过c,看c++的还有点恼火,不过希望lz继续连载,我要天天捧场
时间:
2011-8-8 09:48
作者:
qyf404
楼主分析的很好。至于你们信不信,反正我信了。
时间:
2011-8-9 07:53
作者:
yinhexitaiyang
又来一个好贴,谢谢lz.
学习!
时间:
2011-8-9 10:59
作者:
irg723
这更新速度有点慢啊!
时间:
2011-8-9 16:22
作者:
bossop
标题:
你是干什么的,刚开始学C++吗
我们有的谈
时间:
2011-8-10 16:02
作者:
matrowang
谢谢分享。
时间:
2011-8-15 14:04
作者:
ironman2006
赶紧继续连载呀
时间:
2011-8-22 11:23
作者:
bird1015
怎么没有继续更新了呢?
时间:
2011-8-25 20:43
作者:
663799675
标题:
阿斯顿撒旦
实打实大队的
时间:
2011-8-27 22:43
作者:
songchy008
时间:
2011-8-31 19:11
作者:
好男人我是
顶后再看,楼主威武。
时间:
2011-8-31 21:27
作者:
涂国强
好东西!学习的好资料
时间:
2011-9-2 10:51
作者:
yadodo
很好,加油啊
时间:
2011-9-3 23:04
作者:
mt8870
标题:
顶楼主,期待连载
顶楼主,期待连载
时间:
2011-9-9 19:10
作者:
woi071031
楼主强大!
时间:
2011-9-9 19:46
作者:
二进制
挺感兴趣
时间:
2011-9-10 23:46
作者:
ylibill
C++的咋在这开搞了?
时间:
2011-9-25 12:51
作者:
z7xiaoh
很好,很强大
时间:
2011-10-8 20:22
作者:
nich2009
赞,分析的很不错
时间:
2011-10-9 13:42
作者:
MichaelShang
对齐那里讲得很透彻,支持楼主!!!
时间:
2011-10-11 15:56
作者:
loilih
C++都忘光了 struct 是结构体吧
时间:
2011-10-17 21:16
作者:
lovesky126
还没学过C++,学习一下
时间:
2011-10-18 15:53
作者:
Timberlalalala
很好啊,mark一下
时间:
2011-10-18 19:04
作者:
xddsp
写得很好,支持楼主继续写下去
时间:
2011-11-2 14:39
作者:
raowenlin
顶
时间:
2011-11-3 23:46
作者:
leah2012
这个东西整理出来应该不错哦 多讲讲 受教了
时间:
2011-12-21 12:35
作者:
luo2627
长叹一声~
时间:
2012-3-9 20:06
作者:
chuqingwang
为什么不连载了呢?
时间:
2012-11-29 14:57
作者:
wing_in_sky
mark......
时间:
2014-12-3 23:47
作者:
Dawn_zg
手机看不了代码,郁闷
通信人家园 (https://www.txrjy.com/)
Powered by C114