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