1. 程式人生 > >C++不同編譯單元內定義的非區域性靜態物件的初始化順序

C++不同編譯單元內定義的非區域性靜態物件的初始化順序

靜態物件是指具有靜態儲存期限的物件,即從定義式開始,分配的記憶體空間一直保留到程式結束的物件,包括全域性變數、定義於名稱空間的物件以及使用static修飾符宣告的物件。靜態物件分為兩類,具有程式塊作用域的static物件稱為區域性靜態物件,其餘的成為非區域性靜態物件。

對於在同一個編譯單元(產生單一目標檔案的原始碼,由單一原始檔和其包含的標頭檔案構成)定義的非靜態區域性物件,它們的初始化順序是由其定義順序決定的,而對於在不同編譯單元定義的非靜態區域性變化,它們的初始化順序卻是未定義的,因此是不確定的。

//-*-C++-*-

/***********************************
********* * FileSystem.h * * * * C++靜態非區域性變數初始化順序,案例參考 * * Effective C++ * ********************************************/ #include <cstddef> 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 <iostream> 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 <cstddef>

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 <iostream>

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.