• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            loop_in_codes

            低調做技術__歡迎移步我的獨立博客 codemaro.com 微博 kevinlynx

            最近的兩個問題:less for std::map,靜態變量初始化順序


            說下最近自己遇到的兩個值得讓人注意的問題。
            其一是關于自己給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編譯器自己?

             

             

             


             

            posted on 2008-11-11 17:55 Kevin Lynx 閱讀(7431) 評論(13)  編輯 收藏 引用 所屬分類: c/c++通用編程模塊架構

            評論

            # re: 最近的兩個問題:less for std::map,靜態變量初始化順序 2008-11-11 19:04 空明流轉

            沒有保證初始化順序,要用一些模式手工初始化。  回復  更多評論   

            # re: 最近的兩個問題:less for std::map,靜態變量初始化順序 2008-11-11 19:42 嘯天豬

            第一個問題:STL要求predicator應該是純函數性質,不能有狀態變化;估計是你的實現為了強制這一點,在模板內部做了些手腳吧

            第二個問題:同一TU內,non-local static varible按照定義的順序被初始化,這是標準所規定的  回復  更多評論   

            # re: 最近的兩個問題:less for std::map,靜態變量初始化順序 2008-11-11 19:57 Xw.Y

            1. 沒想法

            2. 全局的靜態變量順序沒有保證。偶也吃過苦頭,查文檔無果。
            通常偶都是在main起來后重新初始化靜態變量。申明用指針而不用實例。
            你的例子太復雜了,
            我印象中這樣就有問題(不過我也可能不正確,這種太容易忘記了)
            //somefile.cpp
            static bool gs_initialized = false;

            class A{
            public:
            A(void) { gs_initialized = true; }
            };

            A InstanceA;

            int main(void){
            // gs_initialized true/false不確定
            }

            問下樓上的,TU是指什么?  回復  更多評論   

            # re: 最近的兩個問題:less for std::map,靜態變量初始化順序 2008-11-12 09:07 Kevin Lynx

            @嘯天豬
            STL predicator不會要求是純虛函數性質,唯一的要求就是這是一個具有operator()性質的東西,普通C函數,重載了operator() 的類均可。我文章里說的問題在于,函數不是:
            bool operator() ( .... ) const // 需要加上const
            {
            }

            TU是不是編譯單元?如果是標準規定,哥們可以給我下文檔鏈接不?

            @Xw.Y

            我的問題同你的本質是一樣的。

              回復  更多評論   

            # re: 最近的兩個問題:less for std::map,靜態變量初始化順序 2008-11-12 09:54 浪跡天涯

            感覺有點印象,后來翻翻EffectiveC++.chm,雜項->條款47:

            對于不同被編譯單元中的非局部靜態對象,你一定不希望自己的程序行為依賴于它們的初始化順序,因為你無法控制這種順序。讓我再重復一遍:你絕對無法控制不同被編譯單元中非局部靜態對象的初始化順序。

            很自然地想知道,為什么無法控制?

            這是因為,確定非局部靜態對象初始化的 " 正確" 順序很困難,非常困難,極其困難。即使在它最普通的形式下 ---- 多個被編譯單元,多個通過隱式模板實例化所生成的非局部靜態對象(隱式模板實例化時,它們本身可能都會產生這樣的問題) ---- 不僅不可能確定正確的初始化順序,往往連找一個可以確定正確順序的特殊情況都不值得。

            在 "混沌理論" 領域,有一個原理稱為 "蝴蝶效應" 。這條原理聲稱,世界某個角落的一只蝴蝶拍動翅膀,會對大氣產生微小的影響,從而導致某個遙遠的地方天氣模式的深刻變化。稍微準確一點來說也就是:對于某種系統,輸入的微小干擾會導致輸出徹底的變化。

            軟件系統的開發也表現了自身的 "蝴蝶效應"。一些系統對需求的細節高度敏感,需求發生細小的變化,實現系統的難易程度就會發生巨大的變化。例如,條款29說明,將一個隱式轉換的要求從 "String到char*" 改為 "String到const char*",就可以將一個運行慢、容易出錯的函數用一個運行快并且安全的函數來代替。

            確保非局部靜態對象在使用前被初始化的問題也和上面一樣,它對你的實現細節十分敏感。但是,如果你不強求一定要訪問 "非局部靜態對象",而愿意訪問具有和非局部靜態對象 "相似行為" 的對象(不存在初始化問題),難題就消失了。取而代之的是一個很容易解決的問題,甚至稱不上是一個問題。

            這種技術 ---- 有時稱為 "單一模式"(譯注:即Singleton pattern,參見 "Design Patterns" 一書)---- 本身很簡單。首先,把每個非局部靜態對象轉移到函數中,聲明它為static。其次,讓函數返回這個對象的引用。這樣,用戶將通過函數調用來指明對象。換句話說,用函數內部的static對象取代了非局部靜態對象。(參見條款M26)

            這個方法基于這樣的事實:雖然關于 "非局部" 靜態對象什么時候被初始化,C++幾乎沒有做過說明;但對于函數中的靜態對象(即,"局部" 靜態對象)什么時候被初始化,C++卻明確指出:它們在函數調用過程中初次碰到對象的定義時被初始化。所以,如果你不對非局部靜態對象直接訪問,而用返回局部靜態對象引用的函數調用來代替,就能保證從函數得到的引用指向的是被初始化了的對象。這樣做的另一個好處是,如果這個模擬非局部靜態對象的函數從沒有被調用,也就永遠不會帶來對象構造和銷毀的開銷;而對于非局部靜態對象來說就沒有這樣的好事。

              回復  更多評論   

            # re: 最近的兩個問題:less for std::map,靜態變量初始化順序 2008-11-12 13:33 嘯天豬

            1 我的意思是predicatory應該像數學函數那樣,不具備狀態變化,而不是pure virtual 這個意思

            你的STL實現為了強制這個語意,總是使用const predicator object,這樣就只能調用operator () const,強制predicator在被使用時無法發生狀態變化。

            STL對predicator的這個要求是語意上的,而不是語法上的;Effective STL上解釋的挺好的

            http://stl.winterxy.com/html/item_39.html


            2 TU就是編譯單元

            參見 "C++ standard 2003: 3.6.2 Initialization of non-local objects"

            http://d.download.csdn.net/source/216275
              回復  更多評論   

            # re: 最近的兩個問題:less for std::map,靜態變量初始化順序 2008-11-12 13:45 luke

            對于靜態變量的初始化順序問題,在thinking in java 的name control 一章里介紹了兩個技巧來出來處理這類問題。  回復  更多評論   

            # re: 最近的兩個問題:less for std::map,靜態變量初始化順序 2008-11-12 13:47 luke

            錯了,是thinking in c++  回復  更多評論   

            # re: 最近的兩個問題:less for std::map,靜態變量初始化順序 2009-06-04 17:56 adie

            1. "另一方面,就STL內部的風格來看,應該不會把predicator處理
            成const &之類的東西,全部是value形式的。奇怪了。"

            STL 內部就是 const & 的,對模版類型參數拷貝代價是未知的,而且拷貝構造函數是否實現都未知,必須是 const& 的. VC8 的 std::less:

            template<class _Ty>
            struct less
            : public binary_function<_Ty, _Ty, bool>
            { // functor for operator<
            bool operator()(const _Ty& _Left, const _Ty& _Right) const
            { // apply operator< to operands
            return (_Left < _Right);
            }
            };

            2. 不同編譯單元的靜態變量初始化順序不確定是因為鏈接器的順序無法保證,和寫的 makefile 有關. 但在同一個編譯單元的靜態變量初始化順序是可以確定的。
              回復  更多評論   

            # re: 最近的兩個問題:less for std::map,靜態變量初始化順序 2009-06-04 20:28 Kevin Lynx

            @adie
            1.我這里說的predicator,指的是less本身。包括傳給find_if的functor,都被我稱為predicator。STL內部保存這種predicator,都是以value的形式保存。既然是value的形式,就不會存在調用這個predicator的operator()必須要求其為bool (*)() const類型的。以前沒搞清楚這個問題,現在也沒關注過。
            你舉的例子中,談的是這個predicator的bool operator()( ...)成員函數的參數為const&。對于less而言,它的這個operator()的參數是map內部保存的element。無論是從效率還是其他方面,是肯定要const&的。

            2.初始化順序這個問題,我對鏈接器細節沒怎么關注過。不過,情況可能真如你所說。謝過。

              回復  更多評論   

            # re: 最近的兩個問題:less for std::map,靜態變量初始化順序 2009-06-05 10:29 adie

            呵呵,因為 predicator 是個模板參數,不存在 const& 的形式,所以理解成了它的參數了。
            從語義上說,確如嘯天豬所言,要求它為 const 雖然不能保證就滿足要求,的確也是合理的。
            從語法上來說,雖然這個比較對象是以 value 的形式保存的,但是使用這個比較對象的函數是 const 的,因此他得到的 this 指針就是 const 的,它的成員 predicator 對象也就是 const 的了。
            至于 Debug 可以編譯通過確實是由于 Debug 和 Release 版的代碼不一樣,Release 比較的時候用的:
            #define _DEBUG_LT_PRED(pred, x, y) pred(x, y)
            Debug 版比較的時候用的:
            #define _DEBUG_LT_PRED(pred, x, y) _Debug_lt_pred(pred, x, y, __FILEW__, __LINE__)
            可以看到 Debug 版本比較的時候用了一個函數,比較對象作為函數參數傳入的時候進行了一次拷貝,拷貝后的對象沒有 const 了。  回復  更多評論   

            # re: 最近的兩個問題:less for std::map,靜態變量初始化順序 2009-06-05 10:36 Kevin Lynx

            @adie
            剛正在看vc2005中的xtree找原因。在_Lbound中,確實是因為_DEBUG_LT_PRED導致DEBUG和RELEASE使用了不同的代碼。而且確實是因為_Lbound是const的才導致this->comp也是const的。但是,在看到你評論之前我還沒反應過來:因為this->comp通過作為函數參數而去掉了const修飾,囧。
            起碼真相大白了。3Q
              回復  更多評論   

            # re: 最近的兩個問題:less for std::map,靜態變量初始化順序 2011-09-21 17:05 pop

            關于ObjMgr.obj和main.obj里都有一個文件靜態變量_t的問題,我想說,既然都是全局的了,為什么還要static呢,難道全局+靜態這樣定義變量_t不會覺得有重復的感覺嗎(靜態其實也是為了數據共享);,是不是靜態的全局變量應該不提倡這種寫法呢;
            另外,對于普通的全局變量定義,都會寫在.cpp內,然后.h是extern它,還得加上#param once防止重復包含,才是標準寫法不是?否則,不出現多個_t才怪~  回復  更多評論   

            精品久久久久久久久中文字幕| 97超级碰碰碰碰久久久久| 久久亚洲色一区二区三区| 久久偷看各类wc女厕嘘嘘| 亚洲婷婷国产精品电影人久久| 久久久国产精品福利免费| 狠狠色婷婷久久综合频道日韩 | 精品久久久无码人妻中文字幕豆芽| 久久精品成人免费观看97| 精品久久久久久久| 久久久噜噜噜久久| 99久久这里只有精品| 亚洲狠狠久久综合一区77777| 久久久久亚洲AV无码专区桃色| 久久国产精品一国产精品金尊| 亚洲AV无码久久| 久久综合香蕉国产蜜臀AV| 色播久久人人爽人人爽人人片aV| 99久久国产热无码精品免费久久久久 | 久久精品aⅴ无码中文字字幕不卡 久久精品aⅴ无码中文字字幕重口 | 一本色道久久HEZYO无码| 国产99久久久久久免费看| 国产福利电影一区二区三区,免费久久久久久久精 | 天天躁日日躁狠狠久久 | 久久久久久亚洲AV无码专区| 久久综合给合综合久久| 2021久久国自产拍精品| 亚洲精品乱码久久久久久久久久久久| 精品国产一区二区三区久久蜜臀| 国产99久久九九精品无码| 久久久久久久人妻无码中文字幕爆| 91麻豆国产精品91久久久| 色狠狠久久AV五月综合| 久久久久久精品久久久久| 国产人久久人人人人爽| 久久综合给久久狠狠97色| 无码八A片人妻少妇久久| 一本久久a久久精品亚洲| 亚洲精品国精品久久99热一| 伊人久久大香线蕉亚洲| 久久夜色精品国产噜噜噜亚洲AV|