C++不同编译单元内定义的非局部静态对象的初始化顺序

静态对象是指具有静态存储期限的对象,即从定义式开始,分配的内存空间一直保留到程序结束的对象,包括全局变量、定义于命名空间的对象以及使用static修饰符声明的对象。静态对象分为两类,具有程序块作用域的static对象称为局部静态对象,其余的成为非局部静态对象。

对于在同一个编译单元(产生单一目标文件的源码,由单一源文件和其包含的头文件构成)定义的非静态局部对象,它们的初始化顺序是由其定义顺序决定的,而对于在不同编译单元定义的非静态局部变化,它们的初始化顺序却是未定义的,因此是不确定的。

//-*-C++-*-

/********************************************
 * FileSystem.h                           *
 *                                          *
 * C++静态非局部变量初始化顺序,案例参考     *
 * Effective C++                            *
 ********************************************/

#include 

class FileSystem
{
private:
  int diskNums;

public:
  FileSystem()
  {
    diskNums = 4;
  }
public:
  std::size_t numDisks() const;
};

extern FileSystem tfs;

/********************************************
 * FileSystem.cpp                           *
 *                                          *
 * C++静态非局部变量初始化顺序,案例参考     *
 * Effective C++                            *
 ********************************************/

#include "FileSystem.h"

std::size_t FileSystem::numDisks() const
{
  return diskNums;
}

FileSystem tfs;

/********************************************
 * Directory.cpp                            *
 *                                          *
 * C++静态非局部变量初始化顺序,案例参考     *
 * Effective C++                            *
 ********************************************/

#include "FileSystem.h"

#include 

class Directory
{
public:
  Directory();
};

Directory::Directory()
{
  std::size_t disks = tfs.numDisks();
  std::cout<<"disks: "<<disks<<std::endl;
}

Directory tempDir;

/********************************************
 * main.cpp                                 *
 *                                          *
 * C++静态非局部变量初始化顺序,案例参考     *
 * Effective C++                            *
 ********************************************/

#include "FileSystem.h"

int main()
{
  return 0;
}

顺序未定义
可见,尽管是Directory.cpp中调用FileSystem对象,但是不同的编译顺序会产生不同的输出结果。

解决方法是用局部静态对象替换非局部静态对象,即将每一个非静态局部对象放到自己专属函数内,并使用static修饰符修饰,然后返回这个对象的引用。当使用这个对象时,用户调用这个函数,而不是直接调用这个对象。

//-*-C++-*-

/********************************************
 * FileSystem.h                           *
 *                                          *
 * C++静态非局部变量初始化顺序,案例参考     *
 * Effective C++                            *
 ********************************************/

#include 

class FileSystem
{
private:
  int diskNums;

  //public:
protected:
  FileSystem()
  {
    diskNums = 4;
  }

public:
  static FileSystem& getFileSystem()
  {
    static FileSystem tfs;
    return tfs;
  }

public:
  std::size_t numDisks() const;
};

//extern FileSystem tfs;

/********************************************
 * FileSystem.cpp                           *
 *                                          *
 * C++静态非局部变量初始化顺序,案例参考     *
 * Effective C++                            *
 ********************************************/

#include "FileSystem.h"

std::size_t FileSystem::numDisks() const
{
  return diskNums;
}

//FileSystem tfs;

/********************************************
 * Directory.cpp                            *
 *                                          *
 * C++静态非局部变量初始化顺序,案例参考     *
 * Effective C++                            *
 ********************************************/

#include "FileSystem.h"

#include 

class Directory
{
public:
  Directory();
};

Directory::Directory()
{
  //  std::size_t disks = tfs.numDisks();
  std::size_t disks = FileSystem::getFileSystem().numDisks();
  std::cout<<"disks: "<<disks<<std::endl;
}

Directory tempDir;

顺序一致
这样,就能保证按照指定的顺序初始化了。

还有一点小问题,就是在多线程时,这种不确定性依然存在,不过可以通过在单线程启动阶段通过手动调用各个非局部静态对象的专属函数来依照一定的初始化顺序初始化各个非局部静态对象。

参考文献

Scott Meyers著,侯捷译. Effective C++中文版. 电子工业出版社. 2012.

你可能感兴趣的:(C++)