#C++ Effective # 条款04-06

4. 确定对象被使用前已先被初始化
读取未初始化的值会导致不明确行为
由于不同情况下初始化的情况是不确定的,因此最好的做法就是永远在使用前初始化。
对于无任何成员的内置类型,必须手工完成初始化,其他类型,就依赖于构造函数了,那么构造函数就需要保证每个成员都得以初始化。

但是请注意不要混淆了初始化与赋值
c++的规定是:对象的成员变量的初始化动作发生在进入构造函数本体之前。所以初始化列表就是个好东西了,而且这种做法比在函数体内部赋值的效率高。
甚至在你想要default构造一个成员变量,也可以使用初始化列表,只要()中为空就行。

规范一下:总是在初始化列表中列出所有的成员变量,以免还得记住哪些成员变量可以无需初值,如果遗漏,导致不明确结果就不好了。

如果成员是const 或者 reference, 那么就一定需要初值,不能被赋值。

许多class拥有多个构造函数,每个函数有自己的成员初始化列表,如果这样的class存在许多成员变量和base class ,多份初始化列表就会导致重复,这时候可以合理地在初始化列表中遗漏那些“赋值表现的和初始化一样好的”成员变量,改用赋值操作,并将赋值操作移到某个函数中,通常是private函数,供所有的构造函数调用,这种做法在“成员变量的值由文件或数据库读入”的时候很有用。

c++有着固定的成员初始化顺序,基类早于派生类,class的成员以其初始化列表中的顺序初始化
那么需要安排好初始化的顺序,如array的初始化需要知道大小,需要初始化大小的量

不同编译单元内定义之non-local static对象
static 对象:其寿命从被构造出来直到程序结束为止。函数内的static对象称为local-static对象,其他static对象就叫non-local static 对象,函数结束,static对象会被自动销毁。
编译单元:指产生单一目标文件  (obj文件)的那些源码,基本上它是单一源码文件加上其所含入的头文件
c++对“定义于不同编译单元内的non-local static对象”的初始化并无明确定义。

所以在构造函数中,不要使用其他编译单元内的non-local static 对象
在其最常见的形式,也就是多个编译单元内的non-local static对象经由“模板隐式具现化”形成,不但不可能决定正确的初始化顺序,甚至往往不值得寻找“可决定正确次序”的特殊情况。
改变这种问题的方法:将每个non-local static 对象搬到自己的专属函数中,该对象在此函数中被声明为static,这些函数返回一个reference指向他所包含的对象,然后用户调用这些函数,不直接指涉这些对象。
——————singleton--------有木有————————
这种手法的基础在于:c++保证,函数内的local static对象会在“该函数被调用期间”“首次遇上该对象之定义式”时被初始化,而你已函数调用替换直接访问,你就可以保证你获得的那个reference将指向一个历经初始化的对象。
而且额外的好处是:你从未调用non-local static对象的“仿真函数”,就绝对不会引发构造和析构成本。

Directory& tempDir()
{
    static Directory td;
    return td;
}
一方面,这种reference-returning函数很简单,是绝佳的inline之选。
另一方面,这些函数内部含有static对象,在多线程系统中有不确定性。任何一种non-local static对象,不论是local or non-local,在多线程环境下“等待某事发生”都会有麻烦。处理这种麻烦:在程序的单线程启动阶段手工调用所有reference-returning函数,可以消除与初始化有关的竞速形势。


结:
为内置型对象进行手工初始化,因为c++不保证初始化他们;
构造函数最好使用成员初始化列表,而不要在构造函数本体内使用赋值操作,初始化列表中变量的顺序与类中声明的顺序一致;
为免除“跨编译单元的初始化次序”的问题,以local static对象替换non-local static 对象。


5. 了解c++默默编写并调用哪些函数
编译器会默认声明一个copy构造函数,一个copy assignment操作符,一个析构函数,如果没有自己声明构造,编译器还会声明一个default构造函数,这些函数都是public inline的。

编译器产出的析构函数是个non-virtual,除非这个class的base class自身声明了virtual析构函数。

c++ 并不允许reference改指向不同对象
所以如果class的成员中含reference成员,必须自己定义copy assignment操作符,他不会自动生成,因为不满足条件。
如果class的成员中含const成员,编译器也会有相同反应,因为修改const不合法

如果基类的copy assignment是private的,那么派生类就不会有自动生成的copy assignment操作符

总结:
编译器可以暗自为class创建default构造函数,copy构造函数,copy assignment操作符,以及析构函数,但是条件不满足时,编译器就会两手一摊,不自动生成了。


6. 若不想使用编译器自动生成的函数,就该明确拒绝
若不想用那些自动生成的函数,可以明确声明一个成员函数,阻止编译器暗自创建其专属版本,而令这些函数为private,使得以成功阻止人们调用。
可以声明为private,而且故意不实现他们,这样的话,member 和 friend也救不了你。

还有一种优雅的做法:
创建一个专门的类
class Uncopyable{
protected:
     Uncopyable() {}
     ~Uncopyable() {}
private:
     Uncopyable(const Uncopyable&);
    Uncopyable& operator= (const Uncopyable&);
}

c++11 拷贝控制,可以在函数的参数列表后面加上 = delete 来告诉编译器,我们不想要这些函数。

然后不想有这些功能的类 private的继承这个UNcopyable基类
这个类包含了很多其他的条款,可以记住

总结:
为驳回编译器自动生成的函数,可以声明为private并且不实现,或者uncopyable这样的基类来做的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值