当前位置:首页 > 工业控制 > 电子设计自动化
[导读]成员变量必须在构造函数的初始化列表中完成初始化。Smart pointer members minimize dependencies while allowing exception safety。 通过以指针存储成员变量的方法最小化依赖 当成员变量的头文件非常大或者非常复杂

成员变量必须在构造函数的初始化列表中完成初始化。Smart pointer members minimize dependencies while allowing exception safety。

通过以指针存储成员变量的方法最小化依赖

当成员变量的头文件非常大或者非常复杂;或者当你有大量的数据成员,并且不想减慢编译速度和强化相互依赖时。你会怎么做?简单来说就是将成员变量保存为指针形式,并用在类的构造函数中使用new为其分配空间。(在某种特殊的情况下,可用引用形式的成员变量代替)。同样要确保在析构函数中删除它们。下面是一段雏形代码。

// User.h

class PointerMember;

class RefParam;

class User

{

public:

User( const RefParam &inParam );

virtual ~User();

private:

PointerMember *mPointerMember;

};

// User.cpp

#include "User.h"

User::User( const RefParam &inParam )

: mPointerMember( new PointerMember( inParam ) )

{

return;

}

User::~User()

{

delete mPointerMember;

return;

}

这样当你要使用成员变量时,原来使用mValMember.Something的地方就要用mPointerMember->Something了。文本编辑器或者集成开发环境的查询替换方法可以很容易地在切换存储方法。

初始化列表

注意,在构造函数初始化列表中初始化对象的指针成员(可以是任何类型成员)是非常重要的。对于C++的初学者来说,像上面的例子中所看到的,下面语句位于大括号之前看起来感觉非常别扭。

: mPointerMember( new PointerMember( inParam ) )

在类对象的生命周期中,如果实际应用时不需要经常使用指针成员变量时,可以选择将该指针成员初始化为nil(注意:删除一个nil指针永远是安全的。因为delete方法的实现在将指针变量传递给堆管理器前,首先检验指针的值)。如果指针变量需要在构造之前分配存储空间的话,一定要在初始化列表中完成,而不像下面代码一样在构造函数体中完成。

User::User( const RefParam &inParam )

{

mPointerMember = new PointerMember( inParam ); // DON'T DO THIS

return;

}

我所工作的大型C++项目中,那些很少使用初始化列表初始化成员变量的,都到处充斥着错误。其中有一个项目,源码共70多兆,我在那家公司工作的时候除了调试错误没做其他任何事情。搞定了一摞错误,又会出现一筐错误。适当的初始化成员变量失败不只是代码的问题,还与更高层次问题相关。

一般来说,构造函数体应该只用来开展对成员变量的操作,或者是全部完成初始化后对整个对象的操作。基本原则是保留函数体给不适合由初始化列表完成的代码。

开始学习适当的使用初始化列表以来,在写信构造函数或者重写老的构造函数后,函数体往往是空的,或者仅包含不多的几行代码,因为全部的实际工作都在初始化列表中完成了。要完成这些工作有时候需要一些额外的工作,但是最后还是能把这些工作量找回来的。

注意了,初始化列表是引用型和常量型成员变量初始化的唯一地方,如果在初始化列表中初始常量成员变量失败了,它可能已在默认构造函数中初始化了,你在其他任何地方都不能改变它,构造函数体也不例外。如果以这种方式初始化引用型成员变量失败,代码就不能通过编译。下面的代码在g++中将发生致命错误。

class HasRefMember

{

public:

HasRefMember( int &inIntToAlias );

private:

int &mSomebodyElsesInt;

};

HasRefMember::HasRefMember( int &inIntToAlias ) // No

initialization list!

{ // refinit.cpp: In method `HasRefMember::HasRefMember(int &)':

// refinit.cpp:11: uninitialized reference member `HasRefMember::mSomebodyElsesInt'

mSomebodyElsesInt = inIntToAlias;// The compiler doesn't even get this far

}

关于初始化列表的其他一些注意事项:在初始化列表中,成员变量的初始化顺序要与类型生命中的顺序一致。实际情况下,C++编译器总是按照变量声明的顺序初始化成员变量。初始化列表顺序与声明相匹配将避免混淆。进一步说,如果你理解了成员变量按照一定的顺序初始化的话,你可以安排顺序使得构造函数的后面的变量使用前面的变量作为参数。如下例所示。

class Example

{

public:

Example( double inVal );

private:

double mSqrt;

double m2Sqrt;

};

Example::Example( double inVal )

: mSqrt( sqrt( inVal ) ),

m2Sqrt( mSqrt * 2 )

{

return;

}

如果我们改变了了成员变量的声明顺序,但是保留初始化列表原样不变的话,m2Sqrt将初始化为内存残留的垃圾值(一个未定义的值),mSqrt将初始化为inVal的平方根。

因此,如果要改变类声明中成员变量的顺序的话,确保同时检验并更新初始化列表。周期性地检验程序中的构造函数,以确保成员变量的正确顺序。一些编译器会非常友好的提示发生的顺序错误。

对于初始化列表中的一些语法方面的局限性,需要另外的一些工作才能行得通。

列表中的每一项都是一次对成员变量构造函数的调用。某些类型看起来没有调用构造函数,但你可以用一个值或者相同类型的对象的引用(你调用了拷贝构造函数)来初始化它。如果你没有重写自己的拷贝构造函数的话,编译器将会提供一个默认的拷贝构造函数(尽管默认构造函数不能提供正确的功能)。仅仅分配了内置数据类型的构造。

构造函数的参数可以仅仅是一串由逗号分割的0值或者表达式,不可以是声明语句、基本块或者对无返回值类型的函数的调用。可以是返回对应数据类型的一个函数(例如传递给拷贝构造函数一个值)。有一点很重要,你不能使用loop循环或者if语句。如果需要的话,就只能放在调用初始化的子过程中了。

不要在初始化列表中调用非静态的成员函数。对象还没有完全创建之前,如果你或者将来某个程序维护者引用一个还没有初始化的成员时,将会导致未定义的行为之类的结果。如果需要写一个子过程来计算得到一个参数给一个构造成员的话,声明该字过程为静态并显示地给他传递它可能用到的参数,但只能传递那些在调用它时已经构造好的参数,而且不要传递this指针。

注意:可能需要调用基类的成员变量,因为基类已经充分的构造好了,还可以调用在其他类中定义的函数,只要不传递this指针作为参数,同样是因为对象还没有完全构造好。

我谨慎的建议,在希望接受的函数是基类的成员函数指针时,是可以传递this指针给初始化列表的。这是因为在子类的构造函数调用时,基类对象已经完全构造好了。这种情况只适用于所用的指针必须为基类指针的情况,相反的情况是不允许的,这正体现了封装——作为一个集成类对象,在它起作用的位置,不能改变基类部分的特征。

有时你可能想要在初始化中调用一个子过程。可能你不想写一个只在一个地方用到的完整的子过程。也可能你不想在类的头文件中声明子过程原型,从而引起所有依赖该头文件的源码的漫长的重编译过程。也可能构造函数需要频繁调用,而你避开子过程调用的开销(这种情况考虑使用内联函数)。还有可能是子过程的返回值是一个很大的对象,构造、拷贝并销毁原对象将导致很大一笔运行时开销。

这里顺带介绍一下条件表达式操作符:“?:”。它时对值进行判断的if语句的缩写,因此,他可以作为一个表达式用于初始化列表。很多人因为感觉他晦涩而不喜欢使用它,我感觉表达式复杂时它能够胜任。但它只是用于初始化列表参数的情况,要不然你就要些一个包含if语句的子过程了。

#include

class InitExample

{

public:

InitExample( std::string const &inFileName, bool inWritable );

private:

int mFileDescriptor;

};

#include

#include

#include

#include

InitExample::InitExample( std::string const &inFileName, bool inWritable )

: mFileDescriptor( open( inFileName.c_str(),

inWritable ? O_RDWR : O_RDONLY ) )

{

if ( mFileDescriptor < 0 )

throw std::exception();

return;

}

到最后,有时候初始化列表变得很长很难阅读。从这一点上来看,就要考虑是否你的类包含了太多的成员。也许寻找一组位于不同类中的自然协作的成员更合适,最后通过组合来实现最初的类。



来源:2008前进0次

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭