說下最近自己遇到的兩個值得讓人注意的問題。
其一是關于自己給std::map寫less predicate,std::map第三個參數是一個典型的functor。map內部將使用
這個functor去判定兩個元素是否相等,默認使用的是std::less。但是為什么傳入的是一個判斷第一個參數
小于第二個參數的functor,而不是一個判斷兩個參數是否相等的functor?按照STL文檔的說法,當檢查兩
個參數沒有小于也沒有大于的關系時,就表示兩個參數相等。不管怎樣,我遇到了需要自己寫這個functor
的需求,于是:
struct MyLess
{
bool operator() ( long left, long right )
{
//...
}
};
DEBUG模式下編譯沒問題,RELEASE模式下卻出現C3848的錯誤。這就有點奇怪了,如果確實存在語法錯誤,
那么DEBUG和RELASE應該一樣才對。查了下MSDN,C3848的錯誤是因為const限定符造成的,如:
const MyLess pr;
pr(); // C3848
于是,在operator()后加上const,就OK了。看了下VC std::map相關代碼,以為是DEBUG和RELEASE使用了不
同的代碼造成。但是我始終沒找到不同點。另一方面,就STL內部的風格來看,應該不會把predicator處理
成const &之類的東西,全部是value形式的。奇怪了。
第二個問題,涉及到靜態變量。這個東西給我的印象特別深刻,因為以前去一家外企應聘時被問到,當時
覺得那個LEADER特別厲害。回來后讓我反思,是不是過多地關注了C++里的花哨,而漏掉了C里的樸素?導致
我至今對純C存在偏好。
說正題,我現在有如下的文件關系:
// def.h
struct Obj
{
Obj()
{
ObjMgr::AddObj( id, this );
}
int id;
};
struct ObjMgr
{
static void AddObj( int id, Obj *t )
{
ObjTable[id] = t;
}
static std::map<int, Obj*> ObjTable;
};
static Obj _t;
// ObjMgr.cpp
#include "def.h"
static std::map<int, Obj*>::ObjMgr ObjTable;
// main.cpp
#include "def.h"
這里舉的例子可能有點不恰當,我在一臺沒有編譯器的機器上寫的這篇文章。忽略掉這些旁支末節。我的意思,
就是想讓Obj自己自動向ObjMgr里添加自己。我們都知道靜態變量將在程序啟動時被初始化,先于main執行之前。
上面代碼有兩個問題:
一、
代碼沒有按照我預期地執行,如果你按照我的意思做個測試,你的程序甚至在進main之前就crash了。我假定你
用的是VC,因為我沒在其他編譯器上試驗過。問題就在于,Obj的構造依賴于ObjTable這個map對象。在調試過程
中我發現,雖然ObjTable擁有了內存空間,其this指針有效,但是,map對象并沒有得到構造。我的意思是,Obj
的構造先于ObjTable構造(下幾個斷點即可輕易發現),那么在執行map::operator[]時,就出錯了,因為這個時候
map里相關數據還沒準備好。
那是否存在某種機制可以手動靜態變量的初始化順序呢?不知道。我最后怎樣解決這個問題的?
二、
在還沒有想到解決辦法之前(不改變我的設計),我發現了這段代碼的另一個問題:我在頭文件里定義了靜態
變量:static Obj _t; 這有什么問題?想想預編譯這個過程即可知道,頭文件在預編譯階段被文本展開到CPP
文件里,然后,ObjMgr.cpp和main.cpp文件里都將出現static Obj _t;代碼。也就是說,ObjMgr.obj和main.obj
里都有一個文件靜態變量_t。
看來,在頭文件里放這個靜態變量是肯定不對的。于是,我將_t移動到ObjMgr.cpp里:
// ObjMgr.cpp
#include "def.h"
static std::map<int, Obj*>::ObjMgr ObjTable;
static Obj _t;
按照這樣的順序定義后,_t的構造居然晚于ObjTable了。也就是說,放置于前面的變量定義,就意味著它將被
首先構造初始化。這樣兩個問題都解決了。
但是,誰能保證這一點特性?C標準文檔里?還是VC編譯器自己?