Effective C++ 条款04_不止于此

确定对象被使用前已先被初始化

像下面这样:
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 对象。

猜你喜欢

转载自blog.csdn.net/weixin_48033173/article/details/108931353
今日推荐