假期通常是個回顧過去的好時候。這裡就是我正在思考的上個月使我產生"ah-hah"瞬間的一個 問題。
問題:
當你編譯下面的C++程序的時候,你可能認為模塊(module)中的對象會先編譯並先初始 化。
這種假設可能得不到預期的結果。
X.h :
#include <string>
using namespace std; // 譯者注: 加上這行
class CObjet {
public:
static const string STRINGX;
};
X.cpp:
#include <X.h>
using namespace std; // 譯者注: 這行可以不要
const string CObjet::STRINGX = "001";
Y.cpp:
#include <iostream>
#include <X.h>
const string STRINGY= CObjet::STRINGX;
int main () {
cout << "CObjet::STRINGX [" <<CObjet::STRINGX << "]" << endl;
cout << "STRINGY [" << STRINGY << "]" << endl;
return 0;
}
如果用下面的命令來編譯:
xlC -c -I./ X.cpp -o X.o
xlC -c -I./ Y.cpp -o Y.o
xlC -o binary X.o Y.o
然後得到的結果如下:
./binary
CObjet::STRINGX [001]
STRINGY [] // 譯者注: 該結果是AIX平台的, Linux平台可能會Segmentation fault
從這個結果來看,STRINGY並沒有像預期的那樣初始化為STRINGX
原因:
STRINGX和STRINGY是全 局靜態對象。STRINGY的初始化取決於STRINGX的初始化。兩者定義在不同的源文件中。
雖然C++語言規范規 定了同一個文件中這類對象的初始化順序(按照定義的順序),但並沒有規定在跨文件或者庫時這些對象的初始 化順序。
因此雖然模塊X.o先編譯, STRINGX仍可能會在STRINGY之後初始化, 這就形成了一個空的 STRINGY.
取決於編譯器和操作系統緩存中當前的值, STRINGY甚至可能包含了垃圾數據並導致程序運行時崩 潰。
解決方案:
為了解決這個問題,一些開發者將每個非局部靜態對象都移動到自己的函數中,並 聲明為靜態的。並讓這些函數返回這個靜態對象的引用, 然後按照自己期望的對象初始化順序來調用這些函數 .雖然這通常是個可移植的方法,但是這需要修改代碼。
XL C/C++編譯器可以使得這項工作變得簡單。你可以使用-qpriority或-qmkshrobj=priority(譯者注: linux上只有-qmkshrobj形式) 或 -Wm,-c選項來指定定義在不同文件或者庫中的靜態對象的初始化順序。這些 選項會給每個模塊賦予一個優先級值,然後根據該值來控制對象的初始化順序。
包含主函數main()的模塊通常優先級為0. 值越小表示優先級越高,在上面的例子中,可以在編譯X.o的時候 指定-qpriority=-100從而保證X.o中的對象在Y.o對象初始化之前初始化。
同樣的,如果你想把X.o創建到一個共享庫中,那麼可以在創建該庫的時候指定-qmkshrobj=-100。
即,你可以用下面的命令來編譯:
xlC -c -I./ -qpriority=-100 X.cpp -o X.o
xlC -c -I./ Y.cpp -o Y.o
xlC -o binary X.o Y.o
或者, 創建共享庫:
xlC -c -I./ X.cpp -o X.o
xlC -c -I./ Y.cpp -o Y.o
xlC -qmkshrobj=-100 -o libX.so X.o
xlC -o binary -btrl Y.o -L. -lX
這時執行程序就會得到如下結果:
./binary
CObjet::STRINGX [001]
STRINGY [001]