确定对象被使用前已先被初始化
像下面这样:
int x;
// x其实也可以认为是一个对象
- 一个实例就是一个对象
class A{
int a;
};
A a;
在某些情况下x被初始化为0;a的成员变量也被初始化为0。
但有时候是不确定的,就会有使用了但没被初始化的情况造成无法挽救的影响,有时候会直接使程序崩掉并不知所以,所以好的程序设计应该像下面这样:
int x = 0; // 定义并初始化
class A{
int x = 0, y = 0; // 定义并初始化
};
A a; // 定义即便没有给参数也不会造成不可估计的影响
另外:
double d;
std::cin >> d; // 可以以读取 input stream 的方式完成初始化
不要在类( class )中混淆了初始化和赋值
看看下面的代码:
class Avg{
int _x;
int _y;
std::string Name;
public:
Avg(const int& x, const int& y, const std::string& name);
};
Avg::Avg(const int& x, const int& y, const std::string& name){
_x = x; //这些都是赋值
_y = y; //而非初始化!
Name = name;
}
构造函数改为下面这样:
Avg::Avg(const int& x, const int& y, const std::string& name)
:_x(x), _y(y), Name(name) //现在就是初始化了
{} //构造函数体内不必做任何操作
记住:
成员变量是const 或者 reference, 他们就一定需要初值,不能被赋值。
总是使用成员初始化,这样做有时候绝对必要,且有往往比赋值更高效。
- class 的成员变量总是以其声明的次序被初始化
下面讲讲更深入的
先做名词解释:
所谓 static 对象,其寿命从被构造出来直到程序结束为止。
所谓编译单元是指产出单一目标文件的那些源码。基本上它是单一源码文件加上其所含入的头文件(#include files)。
假如我们至少用到两个以上源码文件,下面列出两:
class FileSystem { //来自我的程序库
public:
...
std::size_t numDisks() const; //成员函数
...
};
extern FileSystem tfs; //声明一个外部对象,预备给客户使用;
//tfs 代表 "the file system"
此时 FileSystem 对象绝不是一个无关痛痒的对象,因为你的客户如果在 tfs 构造完成之前就使用它,会造成严重的后果。
假设某客户创建一个 class 来处理文件系统下的目录(directories)。很自然会用到 FileSystem 创建的对象:
class Directory{
public:
Directory(params);
...
};
Directory::Directory (params)
{
...
std::size_t disks = tfs.numDisks(); //使用到 tfs 对象
}
//假设进一步,这些客户决定创建一个Directory 对象,用来存放临时文件:
Directory tempDir(params); // 为临时文件做的目录
看完以上代码,我们大概可以看出初始化次序的重要性了。
但C++ 对“定义于不同的编译单元内的 non-local static ( extern )对象” 的初始化相对次序无明确定义。
- 但是C++ 保证,函数内的 local static 对象会在“该函数被调用期间” “首次遇上该对象之定义式” 时被初始化。
那么有了以上保证,我们要怎样解决现在这个问题呢,这里介绍一个小小的设计: - 将每个 non-local static 对象搬到自己的专属函数内(该对象在此函数内被声明为 static)。这些函数返回一个 reference 指向它所含的对象。然后用户调用这些函数,而不是指涉这些对象。换句话说,non-local static 对象被 local static 对象替换了。
把上面的代码修改后:
class FileSystem {
public:
...
std::size_t numDisks() const; //成员函数
...
};
FileSystem& tfs(){ //这个函数用来替代 tfs 对象
static FileSystem fs; //定义并初始化一个 local static 对象
return fs; //返回一个 reference 指向上述对象
}
class Directory{
public:
Directory(params);
...
};
Directory::Directory (params)
{
...
std::size_t disks = tfs().numDisks(); //改为调用tfs()函数
}
Directory& tempDir() //这个函数用来替代 tempDir 对象
{
static Directory td; //定义并初始化一个 local static 对象
return td; //返回一个 reference 指向上述对象
}
修改后这个系统程序的客户完全可以像以前那样使用它,唯一不同的是现在使用 tfs() 和 tempDir()。也就是说他们使用函数返回的 “指向 static 对象” 的reference,而不再使用 static 对象本身。
最后请记住:
- 为内置型对象进行手工初始化,因为C++不保证初始化它们。
- 构造函数最好使用成员初值列,而不要在构造函数本体内使用赋值操作。初值列列出的成员变量,其排列顺序应该和它们在类中的声明次序一致。
- 为免除“ 跨编译单元之初始化次序 ”问题,请以 local static 对象替换 non-local static 对象。