条款4:确定对象被使用前已经被初始化
1、对于类成员变量不提供初始化的行为,要完全避免
class Point
{
private:
int x;
int y;
};
Point p;
2、对于自定义的class或者struct类型,初始化落到构造函数身上!
首先,需要区分初始化和赋值两个概念。
案例如下:
class PhoneNumber{};//...
//这种叫做赋值
class ABEntry
{
public:
ABEntry(string& name, string& address, list<PhoneNumber>& phones)
{
m_name = name;
m_address = address;
m_phones = phones;
}
private:
string m_name;
string m_address;
list<PhoneNumber> m_phones;
};
//下面这种才叫初始化
class ABEntry
{
public:
ABEntry(string& name, string& address, list<PhoneNumber>& phones)
:m_name(name),m_address(address),m_phones(phones)
{
}
private:
string m_name;
string m_address;
list<PhoneNumber> m_phones;
};
为什么要这样做呢?两者的结果不都一样么?
因为对于非POD类型的类,往往使用初始化列表效率会比赋值要高
为什么呢?
答: 基于赋值的操作,我们需要先对成员调用default构造对其进行赋值,然后又进入构造再次赋新的值,相当于弄了两次。而初始化列表就弄一次,结果很明显!!!
注意: 这种理论对于内置的类型其实是行不通的!内置的效率倒是差不多!
3、C++有着十分固定的成员初始化次序。
- 基类早于派生类被初始化
- 类的成员变量总是以声明的次序被初始化,与初始化列表的顺序无关!
4、跨编译单元的非局部静态变量的初始化次序问题
问题: 如果编译单元内的某非局部静态对象初始化使用了另一个编译单元的某个非局部静态对象,它这个用到的对象可能尚未被初始化?
答: 因为c++对“定义不同编译单元的非局部静态对象”的初始化次序是无明确定义的。
以下实例帮助理解:
//FileSystem.h
class FileSystem
{
public:
//...
unsigned numDisks() const;
//...
};
extern FileSystem tfs;
//Directory.h
class Directory()
{
public:
Directory(params);
//...
}
Directory::Directory(params)
{
//这里就依赖别的模块的全局对象
unsigned disks = tfs.numDisks();
//...
}
//这里实例化的时候,就可能会出现问题
Directory tempDir(param);
我们发现只有当tfs在tempDir之前被初始化才可能执行成功。
解决方法: 将非局部静态对象搬运到专属函数内部,该对象在函数内定义为静态的,函数返回一个自己的引用。然后用户调用这些函数来替换直接操作对象。
简单的来说: 非局部静态变量---->局部静态对象 ,调用对象---->调用该函数。
这个方法的基础: 函数内的静态变量会在该函数第一次被调用时被初始化,因此保证了获得的对象是初始化过的!并且如果我们没有调用,则绝不会引发构造成本。
改进案例如下:
//FileSystem.h
class FileSystem {};
FileSystem& tfs() //调用这个
{
static FileSystem fs;
return fs;
}
//Directory.h
class Directory {};
Directory::Directory(params)
{
//...
unsigned disks = tfs().numDisks();
//...
}
Directory tempDir(param);
但是这种结构下的方案下,还存在多线程系统中的不确定性。
处理的方法: 程序的单线程阶段手动调用所有的该类似函数,消除与初始化相关的“竞争”
总结:
- 一定记住对类的每个成员进行初始化,c++本身不保证初始化
- 构造函数最好使用初始化列表进行初始化,能少用赋值就少用。初始化列表的初始顺序最好和在class中声明顺序相同,避免误会
- 通过局部静态替换非局部静态,来消除跨编译单元的初始化次序问题!
结尾: 我是航行的小土豆,喜欢我的程序猿朋友们,欢迎点赞+关注哦!希望大家多多支持我哦!有相关不懂问题,可以留言一起探讨哦!
如有引用或转载记得标注哦!