




已阅读5页,还剩3页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
C+有如下几条:1构造函数初始化列表的变量优先于构造函数(至少明显的写在前面) (若都在初始化列表中初始化,则按声明顺序初始化,与初始化列表中的顺序无关)2静态成员变量先于实例变量3父类成员变量先于子类成员变量4父类构造函数先于子类构造函数java和C#语言1 类成员变量初始化先于类的构造函数2 静态成员变量先于实例变量3 父类成员变量先于子类成员变量 (C#相反)4 父类构造函数先于子类构造函数/认识成员初始化列表(转载)一、 成员初始化列表的位置。成员初始化列表的位置位于构造函数的函数体和参数表之间。通过成员初始化表,类数据成员可以被显式初始化。成员初始化表是由逗号分隔的成员/名字实参对。例如下面的双参数构造函数的实现就使用了成员初始化表。_name是string 型的成员类对象。1inlineAccount:Account(constchar*name,doubleopening_bal)2:_name(name),_balance(opening_bal)345_acct_nmbr=get_unique_acct_nmbr();6成员初始化表跟在构造函数的原型后,由冒号开头。成员名是被指定的。后面是括在括号中的初始值,类似于函数调用的语法。如果成员是类对象则初始值变成被传递给适当的构造函数的实参。该构造函数然后被应用在成员类对象上。在我们的例子中,name 被传递给应用在_name 上的string 构造函数。_balance 用参数opening_bal 初始化。说明: 在这种情况下,string 的拷贝构造函数被调用。把成员类对象_name 初始化成string 参数name.二、 使用初始化表和在构造函数内使用数据成员的赋值之间有什么区别?inline Account:Account( const char *name, double opening_bal ) : _name( name ), _balance( opening_bal ) _acct_nmbr = get_unique_acct_nmbr();inline Account:Account( const char *name, double opening_bal ) _name = name; _balance = opening_bal; _acct_nmbr = get_unique_acct_nmbr();这两种实现有区别吗?两种实现的最终结果是一样的。在两个构造函数调用的结束处三个成员都含有相同的值。区别是成员初始化表只提供该类数据成员的初始化。在构造函数体内对数据成员设置值是一个赋值操作。我们可以认为构造函数的执行过程被分成两个阶段:隐式或显式初始化阶段以及一般的计算阶段。计算阶段由构造函数体内的所有语句构成,在计算阶段中数据成员的设置被认为是赋值而不是初始化。没有清楚地认识到这个区别是程序错误和低效的常见源泉. 初始化阶段可以是显式的或隐式的取决于是否存在成员初始化表。隐式初始化阶段按照声明的顺序依次调用所有基类的缺省构造函数然后是所有成员类对象的缺省构造函数。1inlineAccount:Account()23_name=;4_balance=0.0;5_acct_nmbr=0;6则初始化阶段是隐式的。在构造函数体被执行之前先调用与_name 相关联的缺省string构造函数。这意味着把空串赋给_name 的赋值操作是没有必要的。对于类对象,在初始化和赋值之间的区别是巨大的。成员类对象应该总是在成员初始化表中被初始化而不是在构造函数体内被赋值。缺省Account 构造函数的更正确的实现如下:inline Account:Account() : _name( string() ) _balance = 0.0; _acct_nmbr = 0;它之所以更正确,是因为我们已经去掉了在构造函数体内不必要的对_name 的赋值。但是对于缺省构造函数的显式调用也是不必要的。下面是更紧凑但却等价的实现:inline Account:Account() _balance = 0.0; _acct_nmbr = 0;剩下的问题是:对于两个被声明为内置类型的数据成员其初始化情况如何?例如用成员初始化表和在构造函数体内初始化_balance 是否等价?回答是不。对于非类数据成员的初始化或赋值除了两个例外,两者在结果和性能上都是等价的。即更受欢迎的实现是用成员切始化表:/ 更受欢迎的初始化风格inline Account: Account(): _balanae( 0.0 ), _acct_nmbr( 0 ) 两个例外是:指任何类型的const 和引用数据成员。const 和引用数据成员也必须是在成员初始化表中被初始化,否则就会产生编译时刻错误。例如下列构造函数的实现将导致编译时刻错误:class ConstRefpublic: ConstRef( int ii );private: int i; const int ci; int &ri;ConstRef: ConstRef( int ii ) / 赋值 i = ii; / ok ci = ii; / 错误: 不能给一个 const 赋值 ri = i; / 错误 ri 没有被初始化当构造函数体开始执行时,所有const 和引用的初始化必须都已经发生。只有将它们在成员初始化表中指定,这才有可能。正确的实现如下:/ ok: 初始化引用和 constConstRef:ConstRef( int ii ):ci( ii ), ri( i ) i = ii; 每个成员在成员初始化表中只能出现一次,初始化的顺序不是由名字在初始化表中的顺序决定而是由成员在类中被声明的顺序决定的。例:class Accountpublic:/ .private: unsigned int _acct_nmbr; double _balance; string _name;下面是该类的缺省构造函数:inline Account:Account(): _name( string() ), _balance( 0.0 ), _acct_nmbr( 0 )的初始化顺序为acct_nmbr , _balance 然后是_name 。(由类体内声明的次序决定的。)但是在初始化表中出现或者在被隐式初始化的成员类对象中的成员,总是在构造函数体内成员的赋值之前被初始化。例如:inline Account:Account( const char *name, double bal ) : _name( name ), _balance( bal ) _acct_nmbr = get_unique_acct_nmbr();初始化的顺序是_balance , _name 然后是_acct_nmbr。(为什么?因为_balance 在类体内的声明在 _name之前,_acct_nmbr的声明虽然在他们之前,但是因为他没有出现在成员初始化表中,所以,不用我说了吧 )由于这种实际的初始化顺序与初始化表内的顺序之间的明显不一致有可能导致以下难于发现的错误。当用一个类成员初始化另一个时:class X int i; int j;public: / 喔! 你看到问题了吗? X( int val ):j( val ), i( j ) / .;尽管看起来j 好像是用val 初始化的,而且发生在它被用来初始化i 之前,但实际上是i 先被初始化的。因此它是用一个还没有被初始化的j 初始化的。我们的建议是把用一个成员对另一个成员进行初始化的代码放到构造函数体内。X:X( int val ) : i( val ) j = i; -何谓C+初始化列表与其他函数不同,构造函数除了有名字,参数列表和函数体之外,还可以有初始化列表,初始化列表以冒号开头,后跟一系列以逗号分隔的初始化字段。struct foostring name ;int id ;foo(string s, int i):name(s), id(i) ; / 初始化列表;构造函数的两个执行阶段从概念上来讲,构造函数的执行可以分成两个阶段,初始化阶段和计算阶段,初始化阶段先于计算阶段初始化阶段所有类类型(class type)的成员都会在初始化阶段初始化,即使该成员没有出现在构造函数的初始化列表中计算阶段一般用于执行构造函数体内的赋值操作。下面的代码定义两个结构体,其中Test1有构造函数,拷贝构造函数及赋值运算符,为的是方便查看结果,Test2是个测试类,它以Test1的对象为成员,我们看一下Test2的构造函数是怎么样执行的。class Test1Test1() / 无参构造函数cout Construct Test1 endl ;Test1(const Test1& t1) / 拷贝构造函数cout Copy constructor for Test1 a = t1.a ;Test1& operator = (const Test1& t1) / 赋值运算符cout assignment for Test1 a = t1.a ;return *this;int a ;struct Test2Test1 test1 ;Test2(Test1 &t1)test1 = t1 ;调用代码:Test1 t1 ;Test2 t2(t1) ;输出:Construct Test1Construct Test1assignment for Test1解释一下:第一行输出对应调用代码中第一行,构造一个Test1对象第二行输出对应Test2构造函数中的代码,用默认的构造函数初始化对象test1 / 这就是所谓的初始化阶段第三行输出对应Test2的赋值运算符,对test1执行赋值操作 / 这就是所谓的计算阶段为什么使用初始化列表初始化类的成员有两种方式,一是使用初始化列表,二是在构造函数体内进行赋值操作。主要是性能问题,对于内置类型,如int, float等,使用初始化类表和在构造函数体内初始化差别不是很大,但是对于类类型来说,最好使用初始化列表,为什么呢?由下面的测试可知,使用初始化列表少了一次调用默认构造函数的过程,这对于数据密集型的类来说,是非常高效的。同样看上面的例子,我们使用初始化列表来实现Test2的构造函数。struct Test2Test1 test1 ;Test2(Test1 &t1):test1(t1)使用同样的调用代码,输出结果如下:Construct Test1Copy constructor for Test1第一行输出对应 调用代码的第一行第二行输出对应Test2的初始化列表,直接调用拷贝构造函数初始化test1,省去了调用默认构造函数的过程。所以一个好的原则是,能使用初始化列表的时候尽量使用初始化列表哪些东西必须放在初始化列表中除了性能问题之外,有些时场合初始化列表是不可或缺的,以下几种情况时必须使用初始化列表1.常量成员,因为常量只能初始化不能赋值,所以必须放在初始化列表里面2.引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表里面3. 没有默认构造函数的类类型,因为使用初始化列表可以不必调用默认构造函数来初始化,而是直接调用拷贝构造函数初始化struct Test1Test1(int a):i(a)int i;struct Test2Test1 test1 ;Test2(Test1 &t1)test1 = t1;以上代码无法通过编译,因为Test2的构造函数中test1 = t1这一行实际上分成两步执行:1. 调用Test1的默认构造函数来初始化test12. 调用Test1的赋值运算符给test1赋值但是由于Test1没有默认的构造函数,所谓第一步无法执行,故而编译错误。正确的代码如下,使用初始化列表代替赋值操作struct Test2Test1 test1 ;Test2(Test1 &t1):test1(t1)成员变量的初始化顺序成员是按照他们在类中出
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 硅烷浸渍涂料施工方案
- 派出所保密整改措施下一步工作计划
- 河北拼接围墙施工方案
- 临床营养学护理人卫题库及答案
- 电子信息制造业智能化改造实施策略与2025年投资效益分析报告
- 电商直播运营策略规划深度研究:2025年流量转化策略全案解析
- 挡土的施工方案
- 登革热诊疗方案试卷含答案
- 电商直播行业深度分析报告:2025年运营策略与流量转化策略研究
- 硅pu跑道施工方案
- 2024年东南亚解热镇痛类原料药市场深度研究及预测报告
- 2020年新人教版必修三《Unit 2 Morals and Virtues》单元教案(附导学案)
- 《民航客舱设备操作与管理》课件-项目四 飞机舱门及撤离滑梯
- DL-T 1476-2023 电力安全工器具预防性试验规程
- 2023年10月自考02207电气传动与可编程控制器PLC试题及答案含解析
- 网络自动化运维教程-课程标准
- 项目及其策划方案
- 《食品质量检验分析技术》
- 百家争鸣详解课件
- 肠内营养并发症预防与处理指南
- 宠物医疗行业招商策划
评论
0/150
提交评论