在我自己寫的一個工廠類實現(xiàn)中,每個產(chǎn)品會注冊創(chuàng)建接口到這個工廠類。工廠類使用這些
注冊進(jìn)來的創(chuàng)建接口來完成產(chǎn)品的創(chuàng)建。其結(jié)構(gòu)大致如下:
product *factory::create( long product_type )
{
creator c = m_creators[product_type];
return c();
}
factory::instance().register( PRODUCT_A_TYPE, productA::create );
...
factory::instance().create( PRODUCT_A_TYPE );
這個很普通的工廠實現(xiàn)中,需要寫上很多注冊代碼。每次添加新的產(chǎn)品種類時,也需要修改
這些的注冊代碼。而恰好,這些注冊代碼可能會被放在一個統(tǒng)一的地方。為了消除這個地方
,我使用了偶然間看到的<Modern C++ design>里的做法:
const bool _local = factory::instance().register( PRODUCT_A_TYPE,...
也就是說,通過對全局常量_local的自動初始化,來自動完成對該產(chǎn)品的注冊。
結(jié)果,因為這些代碼全部被放置于一個靜態(tài)庫。最終的代碼文件結(jié)構(gòu)大致為:
lib
- product_a.cpp : 定義了全局常量_local
- product_a.h
- factory.cpp
- factory.h
exe
- main.cpp
現(xiàn)在看起來世界很美,因為factory甚至不知道世界上還有個跟上層邏輯相關(guān)的product_a。
這種模塊耦合幾乎為0的結(jié)構(gòu)讓我竊喜。
悲劇的事情首先發(fā)生于,開VC調(diào)試器,發(fā)現(xiàn)打在product_a.cpp里的斷點失效。就是那個總
是提示說沒有為該文件加載調(diào)試符號。開始還不在意,以為又是代碼和調(diào)試符號文件不匹配
的原因,折騰了好久,不得其果。
后來分析了下,發(fā)現(xiàn)這個調(diào)試提示,就像我開著調(diào)試器打開了一個非本工程的代碼文件,而
斷點就打在這個文件里一樣。也就是說,VC把我product_a.cpp當(dāng)成不是這個工程里的代碼
文件。
按照這個思路寫些實驗代碼,最終發(fā)現(xiàn)問題所在:VC鏈接器根本沒鏈接進(jìn)product_a.cpp里
的代碼。表現(xiàn)出來的情況就是,該編譯單元里的全局常量(全局變量一樣)根本沒有得到初
始化,因為我跟到factory::register并沒有被調(diào)用到。為什么VC不鏈接這個編譯單元對應(yīng)
的目標(biāo)文件?或者說,為什么VC不初始化這個全局常量?
原因就在于,product_a.cpp太獨立了。一個在整個編譯鏈接階段都無法確定該文件是否被
使用的文件,VC就直接不鏈接了。相反,當(dāng)在factory.cpp里寫下類似代碼:
void test()
{
product_a obj;
}
雖然說test函數(shù)不會被調(diào)用,一切情況也變得正常了。好了,不扯了,給最后我的結(jié)論:
1、如果靜態(tài)庫中某個編譯單元在編譯階段被確認(rèn)為它并沒有被外部使用,那么當(dāng)這個靜態(tài)
庫被鏈接進(jìn)可執(zhí)行文件時,鏈接器忽略掉該編譯單元里的代碼,那么,鏈接器自然也不會為
該編譯單元里出現(xiàn)的全局變量常量生成初始化代碼(關(guān)于這部分初始化代碼可以閱讀
<linker and loader>一書);
2、上面那條結(jié)論存在一種傳染性,意思是,當(dāng)可執(zhí)行文件里的代碼使用到靜態(tài)庫中文件A里
的代碼,A里又有地方使用到B里的代碼,那么B依然會被鏈接。這種依賴性,應(yīng)該可以讓編
譯器在編譯階段就發(fā)現(xiàn)(顯然,上面我舉的例子里,factory只有在運(yùn)行期間才會依賴到
product_a.cpp里的代碼)