Item 4:确保对象在使用前被初始化

芝士wa
2024.3.30
Item4链接


“ 在C++中,读取一个未初始化的值会引起未定义行为,在一些平台上,读一个未初始化值会引起程序终止,更可能的情况是得到一个你所读的那个位置上的semi-random bits(半随机二进制位),最终导致不可预测的程序行为和恼人的调试。”

初始化列表

通过构造函数进行初始化
坏的例子:

class ABEntry {   // ABEntry = "Address Book Entry"

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 TimesConsulted;

};

ABEntry::ABEntry(const std::string& name, const std::string& address,
                 const std::list<PhoneNumber>& phones)

{

  theName = name;                       // these are all assignments,

  theAddress = address;                 // not initializations

  thePhones = phones;

  numTimesConsulted = 0;

}

这种初始化的方法是通过在函数体内复制,但还不是最好的做法。C++ 的规则规定一个 对象的数据成员在进入构造函数的函数体之前被初始化。在 ABEntry 的构造函数内,theName,theAddress 和 thePhones 不是被初始化,而是被赋值。

初始化发生得更早——在进入 ABEntry的构造函数的函数体之前,它们的 default constructors(缺省的构造函数)已经被自动调用。但不包括 numTimesConsulted,因为它是一个 built-in type(内建类型)。不能保证它在被赋值之前被初始化。

一个更好的方法是用 member initialization list(成员初始化列表)来代替 assignments(赋值),写法如下:

ABEntry::ABEntry(const std::string& name, const std::string& address,
                 const std::list<PhoneNumber>& phones)

: theName(name),
  theAddress(address),                  // these are now all initializations
  thePhones(phones),
  numTimesConsulted(0)

{}                                      // the ctor body is now empty

这种写法通常具有更高的效率,因为基于赋值的版本会首先调用缺省构造函数初始化theName,theAddress和thePhones,然而又进行了赋值操作,那些缺省构造函数所做的工作被浪费了。初始化列表的方法避免了这个问题,只需要调用依次拷贝构造函数。


初始化顺序

对于多文件编程的场景,如果一个文件用到了另一个文件中的全局变量,无法确定初始化顺序,可能会导致错误。
解决办法:

  • 将全局静态对象移到自己的函数内部
  • 在函数内部将其声明为局部静态对象
  • 函数返回对局部静态对象的引用

工作原理:
C++ 保证局部静态对象的初始化发生在因为调用那个函数而第一次遇到那个对象的定义时。

class FileSystem {                    // from your library

public:
  ...
  std::size_t numDisks() const;       // one of many member functions
  ...
};

extern FileSystem tfs;                // object for clients to use;
                                      // "tfs" = "the file system"
FileSystem& tfs()                   // this replaces the tfs object; it could be
{                                   // static in the FileSystem class
  static FileSystem fs;             // define and initialize a local static object
  return fs;                        // return a reference to it
}

class Directory { ... };            // as before

Directory::Directory( params )      // as before, except references to tfs are
{                                   // now to tfs()
  ...
  std::size_t disks = tfs().numDisks();
  ...
}

Directory& tempDir()                // this replaces the tempDir object; it
{                                   // could be static in the Directory class
  static Directory td;              // define/initialize local static object
  return td;                        // return reference to it
}

总结

手动初始化 built-in type(内建类型)的 objects(对象),因为 C++ 只在某些时候才会自己初始化它们。

在 constructor(构造函数)中,用 member initialization list(成员初始化列表)代替函数体中的 assignment(赋值)。initialization list(初始化列表)中 data members(数据成员)的排列顺序要与它们在 class(类)中被声明的顺序相同。

通过用 local static objects(局部静态对象)代替 non-local static objects(非局部静态对象)来避免跨 translation units(转换单元)的 initialization order problems(初始化顺序问题)。

posted @ 2024-03-30 14:03  芝士wa  阅读(49)  评论(1)    收藏  举报