关于内置类型和STL的初始化就不多说了,下面主要来说下本节我认为的重点以及难点,对于难理解的部分,我尽量会试着实现来帮助理解。
重点1:自定义类的初始化
对于自定义类的初始化,很多人可能会混淆赋值和初始化。以下面的类为例:
class PhoneNumber {};
class ABEntry
{
public:
ABEntry(const string &name, const string &address, const list<PhoneNumber> &phones);
private:
string theName;
string theAddress;
list<PhoneNumber> thePhones;
int numTimesConsulted;
};
ABEntry::ABEntry(const string &name, const string &address, const list<PhoneNumber> &phones)
{
theName = name;
theAddress = address;
thePhones = phones;
numTimesConsulted = 0;
}
C++规定对象的成员变量的初始化动作发生在进入构造函数本体之前。上面的构造函数中,各成员变量(除numTimesConsulted)都不是被初始化,而是被赋值。numTimesConsulted是内置类型,不保证在赋值动作之前获得初值。
一般成员变量的初始化用初始化成员列表来实现:
ABEntry::ABEntry(const string &name, const string &address, const list<PhoneNumber> &phones)
:theName(name),
theAddress(address),
thePhones(phones),
numTimesConsulted(0)
{}
这个构造函数和上一个结果相同,但效率较高。注意:如果成员变量是const或references,他们就一定需要初值,而不能被赋值。
还有一个问题注意下,当使用初始化列表时,初始化变量的顺序与列表的排列顺序没有关系,值取决于声明这些成员变量的顺序。
难点1:不同编译单元内定义之non-local static对象的初始化次序 (什么鬼啊???我想静静!!!)
static是静态的意思,表示这个变量不由栈分配,而存储在特有的全局变量/静态变量区域,具有长寿命的特点(从被构造出来,知道程序结束,才会由系统释放资源)。程序结束时static对象自动销毁,即他们的析构函数会在main函数结束时被自动调用。
non-local说明这个对象是全局的,而不是函数内的静态变量,说明它的范围广。
编译单元:当一个c或cpp文件在编译时,预处理器首先递归包含头文件,形成一个含有所有信息的单个源文件,这个源文件就是一个编译单元。
比如在文件1中定义了
int a=1;
在文件2中又会去使用
extern int a;
int b=a*3;
可以看到文件2应该在文件1后执行,否则b得到的值将是垃圾值(事实上不会,因为a是内置类型,编译器会初始化为0,如果是自定义类型,则会出现b为垃圾值,为了简单说明,这用int代替)。但是C++对不同文件执行的相对次序没有明确定义,这样b的值为多少就不确定了。解决这个问题的办法是,不要让变量有全局作用域,将每个non-local static对象搬到自己的专属函数内(该对象在此函数内被声明为static),这些函数返回一个reference指向它所包含的对象。
可以在文件1中定义:
int& GetA()
{
static int a = 1;
return a;
}
在文件2中调用:
int b = GetA();
这样就能保证a的初始化在前了。