C++ 初始化列表简谈

来源:互联网 发布:简历管理系统 源码 编辑:程序博客网 时间:2024/06/07 06:15

作者:zdd
出处:http://www.cnblogs.com/graphics/
感谢大牛的CSDN 

C++ 初始化列表

何谓初始化列表

与其他函数不同,构造函数除了有名字,参数列表和函数体之外,还可以有初始化列表,初始化列表以冒号开头,后跟一系列以逗号分隔的初始化字段。在C++中,struct和class的唯一区别是默认的访问性不同,而这里我们不考虑访问性的问题,所以下面的代码都以struct来演示。

struct foo{    string name ;    int id ;    foo(string s, int i):name(s), id(i){} ; // 初始化列表};

构造函数的两个执行阶段

构造函数的执行可以分成两个阶段,初始化阶段和计算阶段,初始化阶段先于计算阶段。

初始化阶段

所有类类型(class type)的成员都会在初始化阶段初始化,即使该成员没有出现在构造函数的初始化列表中。

计算阶段

一般用于执行构造函数体内的赋值操作,下面的代码定义两个结构体,其中Test1有构造函数,拷贝构造函数及赋值运算符,为的是方便查看结果。Test2是个测试类,它以Test1的对象为成员,我们看一下Test2的构造函数是怎么样执行的。

复制代码
struct Test1{    Test1() // 无参构造函数    {         cout << "Construct Test1" << endl ;    }    Test1(const Test1& t1) // 拷贝构造函数    {        cout << "Copy constructor for Test1" << endl ;        this->a = t1.a ;    }    Test1& operator = (const Test1& t1) // 赋值运算符    {        cout << "assignment for Test1" << endl ;        this->a = t1.a ;        return *this;    }    int a ;};struct Test2{    Test1 test1 ;    Test2(Test1 &t1)    {        test1 = t1 ;    }};
复制代码

调用代码

Test1 t1 ;Test2 t2(t1) ;

输出

解释一下,第一行输出对应调用代码中第一行,构造一个Test1对象。第二行输出对应Test2构造函数中的代码,用默认的构造函数初始化对象test1,这就是所谓的初始化阶段。第三行输出对应Test1的赋值运算符,对test1执行赋值操作,这就是所谓的计算阶段。

为什么使用初始化列表

初始化类的成员有两种方式,一是使用初始化列表,二是在构造函数体内进行赋值操作。使用初始化列表主要是基于性能问题,对于内置类型,如int, float等,使用初始化类表和在构造函数体内初始化差别不是很大,但是对于类类型来说,最好使用初始化列表,为什么呢?由上面的测试可知,使用初始化列表少了一次调用默认构造函数的过程,这对于数据密集型的类来说,是非常高效的。同样看上面的例子,我们使用初始化列表来实现Test2的构造函数

struct Test2{    Test1 test1 ;    Test2(Test1 &t1):test1(t1){}}

使用同样的调用代码,输出结果如下。

第一行输出对应 调用代码的第一行。第二行输出对应Test2的初始化列表,直接调用拷贝构造函数初始化test1,省去了调用默认构造函数的过程。所以一个好的原则是,能使用初始化列表的时候尽量使用初始化列表。

哪些东西必须放在初始化列表中

除了性能问题之外,有些时场合初始化列表是不可或缺的,以下几种情况时必须使用初始化列表

  • 常量成员,因为常量只能初始化不能赋值,所以必须放在初始化列表里面
  • 引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表里面
  • 没有默认构造函数的类类型,因为使用初始化列表可以不必调用默认构造函数来初始化,而是直接调用拷贝构造函数初始化。

对于没有默认构造函数的类,我们看一个例子。

复制代码
struct Test1{    Test1(int a):i(a){}    int i ;};struct Test2{    Test1 test1 ;    Test2(Test1 &t1)    {        test1 = t1 ;    }};
复制代码

以上代码无法通过编译,因为Test2的构造函数中test1 = t1这一行实际上分成两步执行。

1. 调用Test1的默认构造函数来初始化test1

2. 调用Test1的赋值运算符给test1赋值

但是由于Test1没有默认的构造函数,所谓第一步无法执行,故而编译错误。正确的代码如下,使用初始化列表代替赋值操作。

struct Test2{    Test1 test1 ;    Test2(Test1 &t1):test1(t1){}}

成员变量的初始化顺序

成员是按照他们在类中出现的顺序进行初始化的,而不是按照他们在初始化列表出现的顺序初始化的,看代码。

struct foo{    int i ;    int j ;    foo(int x):i(x), j(i){}; // ok, 先初始化i,后初始化j};

再看下面的代码

struct foo{    int i ;    int j ;    foo(int x):j(x), i(j){} // i值未定义};

这里i的值是未定义的,虽然j在初始化列表里面出现在i前面,但是i先于j定义,所以先初始化i,但i由j初始化,此时j尚未初始化,所以导致i的值未定义。所以,一个好的习惯是,按照成员定义的顺序进行初始化。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 施工企业会计实务 市政工程造价实务 管理学原理与实务课后答案 2016初级会计实务教材 投融资理论与实务 中小企业会计实务 一级建造师市政实务真题 国际贸易理论与实务 建筑工程管理与实务 税务筹划技巧与实务 一级建造师公路实务真题 一级建造师矿业实务 市政公用工程管理与实务 一级建造师建筑工程实务 一级建造师建筑实务真题 金融专业知识与实务 建筑工程管理与实务真题 铁路工程管理与实务 建筑工程管理与实务复习题集 保险专业知识与实务 建筑施工企业会计实务 房地产企业会计实务 建筑工程管理与实务重点 一级建造师水利水电实务 二级建造师公路实务 施工员专业知识与实务 商品流通企业会计实务 证券投资理论与实务 建筑工程管理与实务教材 施工员专业管理实务 二建公路工程管理与实务 二级建造师建筑实务真题 一级建造师建筑实务谁讲的好 工商管理专业知识与实务中级 二建专业工程管理与实务 统计专业知识和实务 二建建筑工程管理与实务 二建机电工程管理与实务 2019年一级建造师市政实务真题 2019年一级建造师建筑实务真题 2019年一级建造师水利实务真题