1. 总是用成员初始化列初始化成员变量。
int x;
//在某些情况下,x可能被初始化为0,也可能不被初始化,出现随机值
class MyClass
{
private:
int x;
int y;
};
...
MyClass p;
//在某些情况下,x,y可能被初始化为0,也可能不被初始化,出现随机值
虽然C++对于何时会自动初始化有一定的规则,但是规则过于繁琐,所以最佳的解决办法就是任何时候都要手动的初始化对象。
//对于没有成员的内置类型,必须手动初始化
int x = 0;
char Buf[1024] = {0};
首先不能混淆初始化和赋值。
C++规定:对象的成员变量的初始化发生在进入构造函数本体之前。
注意:这里说的成员变量必须本身是一个有构造函数的对象,对于内置类型的成员变量C++并不保证。
//赋值操作
class MyClass
{
private:
int SomeValue;
std::string SomeString;
std::list<int> SomeList;
public:
MyClass(const std::string &SomeString, const std::list<int> &SomeList);
};
MyClass::MyClass(const std::string &String, const std::list<int> &List)
{
//在进入此构造函数之前,成员变量SomeString, SomeList已经被自身的默认构造函数初始化
//以下仅为赋值操作
SomeString = String;
SomeList = List;
SomeValue = 0;
}
也就是说,在进入构造函数本体之前,本身是对象的成员变量SomeString,SomeList会被自身的default(默认构造函数)函数初始化,在进入构造函数时,就相当于对对象赋值,而不是初始化,那么之前所有对象的默认初始化都浪费了。
改进方法:使用成员初始列替换赋值操作。
//初始化操作
class MyClass
{
private:
int SomeValue;
std::string SomeString;
std::list<int> SomeList;
public:
MyClass(const std::string &String, const std::list<int> &List);
};
MyClass::MyClass(const std::string &String, const std::list<int> &List)
:SomeString(String), SomeList(List), SomeValue(0) //这些都是初始化操作
{
//函数体内为空
}
此时,MyClass构造函数的实参会成为SomeString, SomeList对象中构造函数的实参,进行copy构造,这样就省去调用若干个default构造函数的时间,效率会有所提升。
对于内置类型成员,初始化和赋值成本相同(但如果内置类型为const或者reference,则必须使用初始化而不可赋值),但为了一致性,也在初始化列中初始化。
如果某个类具有多个构造函数,这些构造函数都有自己的成员初始化列,并且这个类中具有多个成员变量,那么就可能带来大量的重复。解决方法是将那些“初始化和赋值成本相同”的成员变量放在一个private的函数中赋值并供所有构造函数调用。
C++的成员初始化顺序是按照成员的声明顺序进行的,而和成员初始化列的顺序无关。
2. 不同编译单元用local static对象替换non-local static对象。
编译单元:指产生单一目标文件的源码。
local static:指函数内的static对象。
non-local static:除了local static都是non-local static对象。
对于不同编译单元的两个non-local static对象,c++是不保证谁会先被初始化的,如果其中一个对象A需要另外一个对象B初始化,而此时B尚未初始化,就会有所麻烦。
//在文件A中
class MyClassA
{
...
public:
int Fun() const; //成员函数
...
};
extern MyClassA A;
//在文件B中
class MyClassB
{
...
public:
MyClassB();
...
};
MyClassB::MyClassB()
{
int SomeThing = A.Fun(); //调用对象A
}
MyClass B(); //创建一个B类对象
此时初始化次序就先的尤为重要了,如果对象A没有被初始化,直接用来初始化B,程序就会出现意想不到的错误,所以我们需要确定A在B初始化之前已经被初始化。但C++并没有对不同编译单元的两个non-local static对象的初始化次序提供任何保证。
解决方法:将non-local static改成local static
//在文件A中
class MyClassA
{
...
public:
int Fun() const; //成员函数
...
};
MyClassA &A() //用一个函数创建一个local-static对象并且返回其引用
{
static MyClassA a;
return a;
}
//在文件B中
class MyClassB
{
...
public:
MyClassB();
...
};
MyClassB::MyClassB()
{
int SomeThing = A().Fun(); //调用对象A
}
MyClassB &B()
{
static MyClassB b;
return b;
}
C++保证:函数内的local static对象会在其函数调用期间,首次遇到该对象定义式的时候被初始化;所以在MyClassB中,如果以函数的形式调用对象A,则肯定先会经历一次对象A的初始化,从而避免灾难。
另一个优点是:如果从未调用创建对象的函数,那么还会省去对象构造和析构的成本,而non-local static对象必然会耗费这个成本。