《Effective C++》《让自己习惯C++——4、使用变量之前确保已经被初始化》

本文讲述了在C++编程中确保对象在使用前初始化的重要性,讨论了内置数据类型如int和自定义类型如类实例的初始化方式,提倡使用成员初始化列表提高效率。同时,针对none_local_static对象的不确定初始化次序问题,提出了将它们移动到专属函数内的解决方案,如单例模式应用。
摘要由CSDN通过智能技术生成

term4:Make sure that objects are initialized before they’re used

为什么对象使用之前要进行初始化,举个例子

int x;

在某些语境之下,x会被初始化为0。但在其他语境中x的值是不能被保证的。这就会导致不明确的行为,进行给你的程序带来未知的风险,这是需要避免的。未知的情况千变万化,但解决的办法显得“大道至简”——在你使用对象之前,将其初始化即可。

(1)内置数据类型

int x = 0;
const char* text = "A C-style string"; //手工初始化

(2)自定义数据类型

使用成员初始值列表来替换赋值操作:

class PhoneNumber{...};
class ABEntry{
public:
	ABEntry(const std::string &name,const std::string& address,const std::list<PhoneNumber>& phones);
private:
	std::string theName;
	std::string theAddress;
	std::list<PhoneNumber> thePhones;
	int num;
}

//赋值操作
ABEntry(const std::string &name,const std::string& address,const std::list<PhoneNumber>& phones)
{
	theName = name;
	theAddress = address;
	thePhones = phones;
	num = 0;				//赋值操作
}
/*赋值操作其实实施了2个步骤:
(1)调用default构造函数设立初值(default constructor)
(2)再对变量予以赋值(copy assignment)*/
//成员初始化值列表
ABEntry(const std::string &name,const std::string& address,const std::list<PhoneNumber>& phones)
:theName(name);
  theAddress(address);
  thePhones(phones);
  num(0;				//初始化操作
{	}		//构造函数不必有任何的动作

2个操作的结果是相同的,但是成员初值列的操作效率更高。赋值操作首先调用了default构造函数为其中的变量设立了初始值,然后立刻再对他们赋予新的数值。而成员初始化列表操作,只调用了一次copy构造函数是比较高效的。
需要注意的是,成员初始化列表的顺序是相对固定的,为了避免不必要的麻烦,初始化顺序可以遵循声明的次序。

(3)使用local_static对象替换none_local_static对象

书中说的比较晦涩,不同编译单元内定义之non-local static 对象的初始化次序,光看着就很头疼。首先要知道什么是local_static对象,什么是none_local_static对象。
在classes内部,函数内,file作用域内被声明为staic的对象被称为local_static对象;其他static对象被称为none_local_static对象。
而编译单元,指的是单一源码文件加上其所包含的头文件。而不同的编译单元,说明至少涉及2个编译单元;C++对于不同编译单元内的none_local_static对象的初始化次序并没有明确的定义。所以在程序运行过程中,某个编译单元内的none_local_static对象在初始化动作中使用了另一个编译单元内的某个none_local_static对象,他所用到的这个对象还没有初始化,这就带来了问题。
举个栗子:

编译单元A内:
class FileSystem{
public:
	...
	std:size_t numdisks() const;  //成员函数
	...
};
extern FileSystem tfs;
//预留给用户使用对象
 
编译单元B内:
class Directory
public:
{
	Directory(params);
	...
};
Directory::Directory(params){
	...
	std::size_t disks = tfs.numDisks(); //使用tfs对象
}

之前讲过C++对于不同编译单元内的none_local_static对象的初始化次序并没有明确的定义,所以上述程序运行时会出现不可预料的错误。既然无法保证初始化次序,能否明确初始化顺序来达到这一目的呢,C++目前没有机制可以保证这一点,无法做到。
解决方法: 将none_local_static对象搬到一个专属函数内(该对象在函数内被声明为static),这些函数返回一个reference指向他所含的对象。类似于单例模式的手法。
在使用过程中,用户直接调用这些函数,而不是直接调用对象。

编译单元A内:
class FileSystem{...};
FileSystem& tfs(){
	static FileSystem fs;
	return fs;
}
 
编译单元B内:
class Directory{...};
Directory::Directory(params){
	...
	std::size_t disks = tfs().numDisks(); 
	...
}
Directory& tempDir(){
	static Directory td;
	return td;
}

这在单线程中是ok的,如果是在多线程系统中仍然有一定的不确定性,因为他在调用对象的过程中,在等待他进行初始化。这种情况是比较危险的,会有意想不到的错误发生。

2、总结

书山有路勤为径,学海无涯苦作舟。

3、参考

3.1 《Effective C++》

  • 20
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值