[转]深入探索C++对象模型(2)

上一章讲过了关于类对象内存分布,对于nostatic数据将会放在对象内存空间中,static数据成员和nostatic、static函数成员将不会放在对象内存中,对于虚拟继承和含有虚函数的类来说,将会在对象内存中增加一个虚表指针,指向该类的虚表,其中虚表中将会存放虚函数的地址和虚拟基类的地址。一个类中只含有一个共享虚表(继承基类的虚表也是继承类的虚表,一般继承类的虚函数会存放在第一个基类的虚表中方便提取),对象中可以含有多个虚指针,继承至基类,除了直接虚拟继承的继承类才会产生新的vptr和虚表用于指示虚拟基类的位置。

下面是如何构建类对象,即构造函数的深入探索。

首先强调两个C++新手容易陷入的误区:

(1)编译器会为没有显示声明构造函数的类合成默认构造函数;-------------并不是所有的都合成默认构造函数,只有只有四种情况下才会合成,其他情况下不会合成。

(2)编译器合成出来的默认构造函数会为每个数据成员赋予默认值。------------编译器不负责对数据成员的赋值工作,并不是编译器所需的,而应该是程序员完成的工作。(除非其有默认构造函数的subobject和memeber在构造时会初始化)

那么,哪四种情况会导致编译器为类合成一个默认的构造函数呢?

(1)类成员存在默认构造函数的,在合成默认构造函数时必须调用该成员的默认构造函数或者是扩充到已有的默认构造函数中;

(2)所继承的基类存在默认构造函数的,编译器会合成默认构造函数,在构造函数中调用基类默认构造函数;

(3)含有虚函数的类,编译器会合成默认构造函数,主要是因为要初始化虚表指针;

(4)虚继承的类,编译器会合成默认构造函数,也是因为要初始化虚表指针,指向虚拟基类对象。

当已经显示定义了构造函数,即使不是默认的构造函数,编译器也不会为类合成默认构造函数。如果显示定义的构造函数没有完成一些默认构造的功能,编译器将会扩充显示定义的构造函数,调用能够调用的默认构造函数比如成员默认构造函数或基类默认构造函数。

复制构造函数深度探索:

复制构造函数只要用于三个方面:用一个对象的内容构造出一个新对象;参数以值传递的形式传递时;以一个对象作为返回值时。在这三种情况下将会调用复制构造函数,有的情况下需要产生临时对象。

例如:string s=a;将不会产生临时对象,string s=string(a);将会产生临时对象t,然后在调用复制构造函数构造s;string s(a);也不会产生临时对象。

当参数和返回值以值传递的形式,一般情况下都会产生临时对象存储数据,当在编译器NRV优化(下面细讲)的情况下,返回值可能不要产生临时对象,而是直接操作,例如:

<pre>
T operator+(T &a,T &b); Tc=a+b;
</pre>

在编译器优化的情况下,operator +函数极有可能是这个样子的:
<pre>
//编译器内部伪码
void operator +(T &a,T &b,T &c)
{

//直接对c进行操作,这种情况下不需要产生构造对象

}
</pre>

复制构造的两种方式:bitwise和memeberwise,前者是单纯的位拷贝,后者则是以成员为单位进行递归拷贝(所谓递归拷贝就是当成员为一个类对象时将会按照该对象的成员进行拷贝知道结束)。另外,复制构造函数也会对数组的所有成员进行拷贝。

当类中没有显示定义的复制构造函数的时候,编译器会不会合成一个复制构造函数呢?要根据该类的复制构造形式而定,bitwise形式不会合成复制构造函数,memberwise形式会合成。

以下有四种情况会导致类不是bitwise形式,会在无显示定义复制构造函数的时候合成默认复制构造函数:

(1)类成员对象含所有显示复制构造函数(无论是编译器合成的还是自定义的,有时候为了实现NRV优化会自定义复制构造函数),编译器会为这样的类合成默认复制构造函数;

(2)类的基类对象含有复制构造函数,编译器会构造默认复制构造函数,构造的时候将会调用扩充基类复制构造函数;

(3)类中含有虚函数的,编译器将会构造默认复制构造函数,这样可以保证当基类对象被继承类对象初始化的时候,基类对象的vptr指向正确的虚函数表,而不能单纯的复制vptr那么容易,否则将会造成基类对象的vptr指向继承类的虚表了;

(4)虚拟继承的类,编译器将会构造默认复制构造函数,同样是当基类对象被子类对象初始化时,必须保证指向虚拟基类对象的指针被正确设置,因为虚拟基类对象在不同子类内存中的位置是不同的,在虚表中对应offset值是不同的,所以必须保证vptr所指向的虚表的正确性,因此编译器将会为虚拟继承的类合成默认复制构造函数。

NRV编译器优化:前提是编译器提供该服务,且类中存在复制构造函数,无论是编译器合成的还是自定义的,否则优化无从谈起,因为trival复制构造函数就是最有效率的构造方法,有些类为了使用NRV优化甚至强行提供显示复制构造函数。

NRV优化主要用在返回值为对象的函数身上,主要方法是减少临时对象的数目来提高效率。

优化前:
<pre>
X bar()//将会产生临时对象存储返回的值
{

X xx;

//处理

return xx;

}
</pre>

优化后:
<pre>
void bar(X &_result)//这样在指定函数返回对象是将不会产生临时对象

{

X xx;

//处理xx

//调用复制构造函数

_result.X::X(xx);

return;

}
</pre>

X yy=bar();相当于bar(yy);

但是,当bar()单独使用的时候,仍然会产生一个临时对象存储结果,因为函数没有返回的对象。

而NRV优化又是在上述的基础上直接对_result上处理,不需要在函数内部产生一个xx对象,然后再调用复制构造函数,这样就减省了复制构造的消耗,但是会使用默认构造函数。
<pre>
void bar(X &_result)//NRV优化

{

_result.X::X();

//对_result直接进行处理

return;

}
</pre>

成员初始化表的使用:

有以下三种情况必须使用成员初始化表:

(1)调用基类构造函数或者基类复制构造函数

(2)类对象中的引用初始化

(3)类中const对象的初始化

其成员初始化既可以使用成员初始化列表,也可以使用普通的方式,但还是普通方式效率太低。

普通初始化方式:首先将成员默认构造,然后产生一个临时对象使用复制构造参数对象,最后使用赋值方式初始化;

而成员初始化列表则会在用户代码之前按照成员声明的顺序并按相应的方式进行初始化。

注意:

成员初始化列表中的顺序不决定初始化顺序,成员初始化顺序由成员声明的顺序而定;

成员初始化列表中的内容先于用户代码执行

原文地址:http://www.cnblogs.com/tracylee/archive/2012/12/18/2824125.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容

  • 一个博客,这个博客记录了他读这本书的笔记,总结得不错。《深度探索C++对象模型》笔记汇总 1. C++对象模型与内...
    Mr希灵阅读 5,540评论 0 13
  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy阅读 9,484评论 1 51
  • 本文博客园地址:http://www.cnblogs.com/xiongxuanwen/p/4290086.htm...
    先之阅读 824评论 0 5
  • 1. 结构体和共同体的区别。 定义: 结构体struct:把不同类型的数据组合成一个整体,自定义类型。共同体uni...
    breakfy阅读 2,099评论 0 22
  • 阳光正好,我想去春游。我想坐在绿绿的草地上想心事,我想看孩子们追逐打闹,我想把这惬意的时光都拍下来。而事实上,我只...
    她是妖孽阅读 387评论 0 0