条款4:确定对象在使用前已被初始化

考虑如下设计:

 1 #include<iostream>
 2 
 3 using namespace std;
 4 
 5 class Entry
 6 {
 7 public:
 8     Entry(const string& name, const string& address, const string& phone);
 9 
10 private:
11     string name;
12     string address;
13     string phone;
14     int nums;
15 };
16 Entry::Entry(const string& name, const string& address, const string& phone)
17 {
18     this->name = name;            // 构造函数内的这些语句都是赋值而不是初始化
19     this->address = address;
20     this->phone = phone;
21     this->nums = 0;
22 }
23 
24 int main()
25 {
26     Entry entry("name: benxintuzi", "address: 123", "phones: 123456");
27 
28     return 0;
29 }

如上所述,在构造函数体内都为赋值语句,而不是初始化语句。C++规定,对象的成员变量的初始化时机发生在进入构造函数之前,这点非常重要。而对于基本变量类型如nums却不能保证进入构造函数之前将其初始化。上述语句的执行是:首先调用name/address/phone的default构造函数,然后在Entry的构造函数中对其进行赋值,如此一来,调用变量的默认构造函数就变得没有什么意义了,反正最终获得的值都会马上被换掉了。为了避免重复性的工作,利用变量的构造函数总会被调用的原则,将上述Entry的构造函数改进如下:

Entry::Entry(const string& name, const string& address, const string& phone) :

    name(name), address(address), phone(phone), nums(0)

{ }

利用构造函数的初始化列表,虽然结果是相同的,但是效率会提高很多:因为初始化列表只会调用name等变量的copy构造函数,而不会再次调用Entry的赋值操作符函数了,这样Entry的构造函数体就可以做一些更有意义的事了。同样不指定实参的初始化列表同样有用,比如设计一个Entry的无参构造函数如下:

Entry::Entry() :

    name(), address(), phone(), nums(0)

{ }

注意:

对于基本内置类型而言,在构造函数体内赋值与调用初始化列表指定初值,其代价是等同的。但是如果成员变量是const或者references,那么就没办法使用赋值操作了,必须使用初始化列表才能通过编译。所以我们要遵循的规则就是:总是使用初始化列表,这样做最差情况下是等效的,而一般情况下是高效的,甚至有时还是必须的。

 

C++类的成员初始化次序问题:

  • 先基类后派生类。
  • 类中成员变量的初始化次序依赖与声明次序,与初始化列表排列次序无关。
  • 对于不同编译单元中(一个编译单元就是指能够产生单一目标文件的那些源码:包括源码文件、头文件展开、宏展开等构成的一个大文件)的变量引用问题,为了防止引用了一个还没有被初始化的变量(比如说某些资源句柄之类的),最好的办法就是将被引用的变量通过单例模式创建出来,这样就可以保证正确的初始化顺序了
原文地址:https://www.cnblogs.com/benxintuzi/p/4535556.html