规则一 永远在使用对象之前将它初始化

int x = 0;
const char* text = "A C-style string";
double d;
std:: cin >> d;
// 对于内置类型的初始化必须手工完成,其他类型的初始化职责落在构造函数身上。

规则二 确保每一个构造函数都将对象的每一个成员初始化

这个规则很简单,重要的是不要混淆赋值和初始化操作。

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 numTimesConsulted;
}
ABEntry(const std::string& name, const std::string& address, const std::list<PhoneNumber>& phones) {
theName = name;
theAddress = address; // 这些都是赋值
thePhones = phones; // 而非初始化
numTimesConsulted = 0;
}

C++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前。

初始化发生时间更早,发生于这些成员的default构造函数被自动调用之时(比进入ABEntry构造函数本体的时间更早)。numTimeConsulted例外,因为是内置类型。

规则三 member initialization list(成员初值列)替换赋值操作

ABEntry::ABEntry(const std::string& name, const std::string& address, const std::list<PhoneNumber>& phones)
: theName(name),
theAddress(address), // 现在,这些都是初始化(initialization)
thePhones(phones),
numTimeConsulted(0)
{ } // 现在,构造函数本体不必做任何动作

这个构造函数和上一个的最终结果都相同,但通常效率较高。因为赋值那个版本首先调用default构造函数为theName, theAddress和thePhones设初值,然后立刻再对它们赋予新值。default构造函数的一切动作因此浪费了。第二版避免了这个问题,因为初值列中针对各个成员变量而设的参数,被拿出作为各成员变量之构造函数的实参。theName以Name为初值进行copy构造,对于内置类型,其初始化和赋值的成本相同,但是为了一致最好也通过成员初值列来初始化。 同样道理,甚至当你想要default构造一个成员变量,你都可以使用成员初始值,只要指定无物作为初始化实参即可。

ABEntry::ABEntry()
:theName(), // 调用theName的default构造函数
theAddress(),
thePhones(),
numTimesConsulted(0) // 记得将numTimesConsulted显式初始化为0
{}

规则四 C++有着十分固定的"成员初始化次序"

base classes更早于其derived classes被初始化,而class的成员变量总是以其声明次序被初始化,即使在成员初值列中以不同的次序出现,夜不会有任何影响。

规则五 “不同编译单元内定义之non-local static对象”的初始化次序

所谓static对象,其寿命从被构造出来直到程序结束为止,因此stack和heap-based对象都被排除。

函数内的static对象称为local static对象(因为它们对函数而言是local),其他static对象称为non-local static对象。

程序结束时static对象会被自动销毁,也就是它们的析构函数会在main()结束时被自动调用。

所谓编译单元是指产出单一目标文件的那些源码。基本上它是单一源码文件加上其他含入的头文件。

C++对“定义于不同编译单元内的non-local static对象”的初始化次序并无明确定义。如果一个引用了另外一个未初始化的话,就会出错。

class FileSystem {
public:
...
std::size_t numDisks() const;
...
};
// 调用
extern FileSystem tfs;
class Directory {
public:
Directory( params );
...
};
Directory::Directory( params ) {
...
std::size_t disks = tfs.numDisks(); // 使用tfs对象
}
Directory tempDir( params );

除非tfs在tempDir之前先被初始化,否则tempDir的构造函数会用到尚未初始化的tfs。然而,C++对“定义于不同的编译单元内的non-local static对象”的初始化相对次序并无明确定义。

规则六 non-local static 对象被local static对象替换

class FileSystem { ... };
FileSystem& tfs() {
static FileSystem fs;
return fs;
}
class Directory { ... };
Directory::Directory( params ) {
...
std::size_t disks = tfs().numDisks();
...
}
Directory& tempDir() {
static Directory td;
return td;
}

以上设计,如果再把它们的构造函数设置为private的话,就是一种Singleton设计模型

总结

  1. 对内置类型对象进行手工初始化,因为C++不保证初始化它们。
  2. 构造函数最好使用成员初值列,而不要再构造函数本体内使用赋值操作。初值列列出的成员变量,其排列次序应该和它们在class中的声明次序相同。
  3. 为免除“跨编译单元之间初始化次序“问题,请以local static 对象替换non-local static对象。

最新文章

  1. Python 黑帽编程大纲(变化中)
  2. linux下安装jdk
  3. eclipse的安装环境及eclipse下maven的配置安装
  4. Sharepoint学习笔记—习题系列--70-573习题解析 -(Q94-Q96)
  5. [听点音乐]American Music Awards 2015 Winners
  6. 利用TOAD实现把EXCEL数据导入oracle数据库
  7. thrift的简单实现
  8. POJ 1094 Sorting It All Out (拓扑排序) - from lanshui_Yang
  9. Linux中的挂载和卸载
  10. 数据库sql优化方案
  11. ubuntu搭建 zabbix3.2 with mysql database (Ubuntu 14.04.5 LTS)
  12. Spring Security4.1.3实现拦截登录后向登录页面跳转方式(redirect或forward)返回被拦截界面
  13. ginput函数用法
  14. yk-随记
  15. Random Forest总结
  16. ACM:日历本
  17. 实现鼠标悬停,div勾画div边框的动画
  18. RMAN_RAC归档日志备份包恢复到单机
  19. logging模板日志格式
  20. VHDL 例程

热门文章

  1. 酷壳用的还是 Wordpress
  2. xmpp muc 群聊协议 1
  3. less-符号之逗号,空格,父级选择器
  4. Angular ui-route的用法
  5. 网络模型+三次握手+四次挥手+DNS+HTTPS
  6. session_id
  7. Pandas 删除指定列中为NaN的行
  8. python常见模块之collections模块
  9. 序列化组件之生成hypermedialink
  10. java final 关键词