① 初始化不等于赋值
class PhoneNumber;
class ABEntry
{
public:
ABEntry(const string&name, const string&address, const list<PhoneNumber>& phone);
private:
string theName;
string theAddress;
list<PhoneNumber> thePhone;
int numTimesConsulted;
};
ABEntry:: ABEntry(const string& name, const string& address, const list<PhoneNumber>& phones)
{
theName = name;
theAddress = address; //这些都是赋值不是初始化而是调用了一次默认构造函数和一次拷贝赋值运算符
thePhone = phones;
numTimesConsulted = 0;
}
ABEntry::ABEntry(const string& name, const string& address, const list<PhoneNumber>& phones):
theName(name),theAddress(address),thePhone(phones),numTimesConsulted(0){}//调用了一次拷贝构造函数
对大多数类型来说,比起先调用默认构造函数然后再调用一次拷贝赋值运算符,调用一次拷贝构造函数比较高效,有时甚至高效得多。
由于编译器会为用户自定义类型之成员变量自动调用默认构造函数--如果那些成员变量在“成员初值列”中没有被指定初值的话,因而引发某些程序员过度夸张地采用以上写法。那是可以理解的,但请立下一个规则,规定总是在初值列中列出所有成员变量,以免还得记住哪些成员变量(如果它们在初值列中被遗漏的话)可以无需初值。举个例子,由于numTimeConsult额的属于内置类型,如果成员初值列遗漏了它,它就没有初值,因为可能开启“不明确行为“的潘多拉盒子
有些情况下即使面对的成员变量属于内置类型(那么其初始化和赋值的成本相同),也一定得用初值列。是的,如果成员变量是const或references,它们就一定需要初值,不能被赋值。为避免需要记住成员变量何时必须在成员初值列中初始化,何时不需要,最简单的做法就是:总是使用成员初值列。这样做有时候绝对必要,且又往往比赋值更高效。
② 使用初始值列时保证顺序与变量声明顺序一致
③不同编译单元内定义之non-local-static对象的初始化次序
首先解释两个概念:
1.编译单元:简单的说,按照C++标准,每一个.cpp文件就是一个编译单元,编译器不会编译.h头文件。
2.static对象:特点:生命周期从创建一直到程序结束为止。包括:全局(global)对象;定义域命名空间(namespace)作用域内的对象;在class内、函数内以及file作用域内被声明为static的对象。
函数内的static对象就叫做:local static对象。除此之外的static对象就是所谓的non-local static对象。
标题的意思,解释下来,就是在两份.cpp源码中,分别包含有一个non-local static对象,恰好这两个对象要初始化的时候又与另外一个有所关联,这时候就会存在一个初始化次序问题。举个例子就是,源码1中定义了nls(non-local static)对象A,源码2中定义了nls对象B,当B要初始化的时候会用到A的成员,如果在B要初始化的时候A还没有初始化,就会报错了。而C++中对于不同源码中的nls对象的初始化次序问题没有明确定义,也就是A和B初始化谁先谁后根本就是无解的。
解决办法:
按照effective C++所述,可以将每个nls对象搬到自己的专属函数内,在此函数内该对象被声明为static,最后函数返回指向这个新的static对象的引用。具体如下例:
class filesystem{
public:
...
std:size_t fun() const;//众多成员函数之一
...
};
extern filesystem A;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
class directory{
public:
directory(params);
...
};
directory::directory(params)
{
...
std::size_t disks=A.fun();//使用A对象
...
}
directory B(params);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
最后一行代码出现了标题所说的问题,除非A在B之前先被初始化,否则B的构造函数会用到还未初始化的A,但上文已经提到相关的初始化次序是一个无解的问题。
解决办法如下:
class filesystem{...};//同上
filesystem& A() //这个函数用来替换A对象;它在filesystem class中可能是个static
{
static filesystem AA;//定义并初始化一个local static对象
return AA; //返回一个referen指向上述对象
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
class directory{...};
directory::directory(params)
{
...
std::size_t disks=A().fun();
...
}
directory& B()
{
static directory BB;
return BB;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
这样修改后,按照C++的标准,函数内的local static对象会在该函数被调用期间首次遇到该对象之定义式时被初始化,也就是调用函数A时AA对象将已经完成初始化,即解决了本文所述的次序问题。同时如果从未调用此函数,也绝不会产生构造和析构成本,非常划算。