注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

gmd20的个人空间

// 编程和生活

 
 
 

日志

 
 

C++类成员初始化顺序理解错误导致的问题  

2012-05-03 13:22:43|  分类: 程序设计 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
发现了代码里面一个类成员初始化导致的问题,vc2010 release版编译出来程序不像想象的那样工作,仔细比较了汇编代码,找了书来看看,才知道以前的理解错了。C++ 这陷阱非常的多啊。

一直以为成员的初始化按照构造函数里面指定的顺序来的,其实真正的初始化顺序是和这个l列表顺序无关的,是按照头文件里面类的成员的定义的顺序来的。有下面一个这样的类,我之前写的出错的版本是第二个。
-------------------------------------

class RegisteredDelivery{
public:
explicit RegisteredDelivery(unsigned char _smsc_delivery_receipt,
unsigned char _sme_originated_acknowledgement,
unsigned char _intermediate_notification):
smsc_delivery_receipt(_smsc_delivery_receipt),
sme_originated_acknowledgement(_sme_originated_acknowledgement),
intermediate_notification(_intermediate_notification),
registered_delivery(
(smsc_delivery_receipt & 0x03) | /// registered_delivery 的初始化依赖另外一个类成员smsc_delivery_receipt;
((sme_originated_acknowledgement & 0x03) << 2) |
((intermediate_notification & 0x01) << 4)
)
{
}

unsigned char smsc_delivery_receipt;
unsigned char sme_originated_acknowledgement;
unsigned char intermediate_notification;
unsigned char registered_delivery; //这个顺序放在最后,可以保证 smsc_delivery_receipt;的初始化在registered_delivery; 前面。
};

------------------------

RegisteredDelivery registered_delivery(arg->smsc_delivery_receipt,
arg->sme_originated_acknowledgement,arg->intermediate_notification);
00DBFC45 mov al,byte ptr [ebx+62h] //初始化 类成员intermediate_notification;,保存在寄存器al中
00DBFC48 mov dl,byte ptr [ebx+61h] //初始化 类成员sme_originated_acknowledgement;保存在 寄存器dl中
00DBFC4B mov cl,byte ptr [ebx+60h] // 初始化 类成员smsc_delivery_receipt;保存在 寄存器cl中
request_.registered_delivery = registered_delivery.registered_delivery;
00DBFC4E and al,1
00DBFC50 add al,al //左移两位,就是乘以4,都被优化 两个 add 指令,变成加自身4次了。
00DBFC52 add al,al
00DBFC54 and dl,3
00DBFC57 or al,dl
00DBFC59 add al,al // 这里再有一个左移2为的操作。
00DBFC5B add al,al
00DBFC5D and cl,3
00DBFC60 or al,cl
00DBFC62 mov byte ptr [edi+158h],al




=================================================================

class RegisteredDelivery{
public:
explicit RegisteredDelivery(unsigned char _smsc_delivery_receipt,
unsigned char _sme_originated_acknowledgement,
unsigned char _intermediate_notification):
smsc_delivery_receipt(_smsc_delivery_receipt), // 成员初始的顺序和构造函数这里指定的列表顺序没有关系,smsc_delivery_receipt还是在

registered_delivery的后面的。
sme_originated_acknowledgement(_sme_originated_acknowledgement),
intermediate_notification(_intermediate_notification),
registered_delivery(
(smsc_delivery_receipt & 0x03) | //这里依赖了未初始化的成员了,
((sme_originated_acknowledgement & 0x03) << 2) |
((intermediate_notification & 0x01) << 4)
)
{
}

unsigned char registered_delivery; //这个顺序变了,registered_delivery的初始化在成员的smsc_delivery_receipt前面了,
unsigned char smsc_delivery_receipt;
unsigned char sme_originated_acknowledgement;
unsigned char intermediate_notification;

};


---------------

RegisteredDelivery registered_delivery(arg->smsc_delivery_receipt,
arg->sme_originated_acknowledgement,arg->intermediate_notification);
request_.registered_delivery = registered_delivery.registered_delivery;
00DBFC45 movzx edx,byte ptr [esp+0Fh] //直接访问未初始化的类成员intermediate_notification;,构造函数里面初始化类成员intermediate_notification;的三个语句都被忽略掉

了。优化之后就没了。
00DBFC4A movzx eax,byte ptr [esp+0Eh]
00DBFC4F movzx ecx,byte ptr [esp+0Dh]
00DBFC54 and dl,1 //下面使用未经初始化的 类成员来计算类成员registered_delivery;,全部都是错的了。
00DBFC57 add dl,dl
00DBFC59 add dl,dl
00DBFC5B and al,3
00DBFC5D or dl,al
00DBFC5F add dl,dl
00DBFC61 add dl,dl
00DBFC63 and cl,3
00DBFC66 or dl,cl
00DBFC68 mov byte ptr [edi+158h],dl



============================================
看看书上怎么说的 


"The C++ programming language" 
10.4.6 Class Objects as Members [class.m]

The members’ constructors are called before the body of the containing class’ own constructor
is executed. The constructors are called in the order in which they are declared in the class rather
than the order in which they appear in the initializer list. To avoid confusion, it is best to specify
the initializers in declaration order. The member destructors are called in the reverse order of construction.



C++编程思想 Thinking in C++
”14: Inheritance & Composition“ 'The constructor initializer list"

It’s also interesting that the order of constructor calls for member
objects is completely unaffected by the order of the calls in the
constructor initializer list.list. The order is determined by the order that
the member objects are declared in the class.If you could change
the order of constructor calls via the constructor initializer list, you
could have two different call sequences in two different
constructors, but the poor destructor wouldn’t know how to
properly reverse the order of the calls for destruction, and you
could end up with a dependency problem.

这里说了,如果让你在类的初始化列表指定成员的初始化顺序了,那你在不同的两个构造函数就可以指定两种不同的顺序了,但这样弱智的析构函数是不可能知道你不同的顺序的啊,没办法按照你

相反的顺序去销毁啊。所以这个类程序初始化的顺序都固定下来,按照你文件中定义的顺序来初始化了,这样析构函数也只要按照一种顺序反过来释放资源就可以了。
=============================================

这样的话,类里面的一个成员的初始化最好不要去倚赖类的另外的成员了,不然顺序需要手工在文件里面确保定义的顺序,因为类成语的初始化就是按照文件里面定义的顺利来的,和构造函数里面

的初始化列表无关。特别是你有几个构造函数的时候,成员初始化互相依赖那就更没法做到了。
所以上面的代码还是改成这样比较好,




class RegisteredDelivery{
public:
explicit RegisteredDelivery(unsigned char _smsc_delivery_receipt,
unsigned char _sme_originated_acknowledgement,
unsigned char _intermediate_notification):
smsc_delivery_receipt(_smsc_delivery_receipt),
sme_originated_acknowledgement(_sme_originated_acknowledgement),
intermediate_notification(_intermediate_notification),
registered_delivery(
(_smsc_delivery_receipt & 0x03) | //registered_delivery的初始化不再依赖smsc_delivery_receipt;等成员了,而是使用的构造函数的参数了
((_sme_originated_acknowledgement & 0x03) << 2) |
((_intermediate_notification & 0x01) << 4)
)
{
}

unsigned char registered_delivery;
unsigned char smsc_delivery_receipt;
unsigned char sme_originated_acknowledgement;
unsigned char intermediate_notification;
};






  评论这张
 
阅读(1017)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017