說幾個STL的缺點吧,雖然都是在比較極端的狀況下出現,可是對於一些大項目仍是會遇到的
1. 代碼膨脹問題
每個實例化過的模板類,都會膨脹出一份獨立的代碼,好比
std::vector<std::string>, std::vector<int>,編譯後會產生兩份代碼,在VC2008下,每份代碼大約是3-4kb,這是由於vector比較簡單代碼少,若是是map則會產生30-50kb的代碼,由於map裏有個複雜的紅黑樹。對於數據處理類的代碼裏通常會定義不少種不一樣的結構體,不一樣的結構體放到不一樣的容器裏,就會實例化出不少個類的代碼,我見過一個項目裏,這樣的vector就有數百個。
2. 內存使用效率問題 (以vc++2008爲例)
stl在內存使用效率上是比較低效的,好比std::string,它的sizeof大概是28,由於它有一個內置的16字節數組,用來作小字符串優化的,就是說低於16字節的字符串都會至少佔用28字節內存,若是恰好17字節字符串,則會佔用28字節+額外分配的字符串內存,額外分配的內存是一個堆塊,又有不少浪費,相比用一個char *存儲字符串大約多佔用了一倍內存。
還有map<>,每個map的node都是一塊獨立分配的內存,若是是 map<int, int>呢,那就很悲劇了,爲了存一個int要消耗幾十個字節,很浪費的。
若是元素數量有百萬級,那麼內存佔用就很可觀了,這種狀況下建議本身實現allocator,作內存池。
3. deep copy問題
讓兩個容器的實例作賦值操做,看起來就一條語句,實際上容器裏的每一個元素都執行了一次賦值操做。若是容器裏有百萬級的數據,那麼一個等號就產生了幾百萬次的構造和析構。
傳遞參數的時候必定要用 const 引用,賦值能夠用 swap代替。
4. 隱式類型轉換
好比 有個函數
void doSomething(const std::string &str);
調用的時候
doSomething("hello");
能編譯執行,可是會產生一個臨時的匿名的std::string實例,把"hello"複製一遍,而後在調用完成後析構掉。若是這個發生在循環體內部有可能影響性能。
以上這些問題,在小程序裏或者數據規模不大的時候,好比容器內元素只有幾千這個規模,都不是什麼大問題,那時開發效率纔是重點,可是一旦有大數據stl容器會成爲性能瓶頸的。
我並非主張不用STL,而是要充分了解STL的優缺點,根據應用場景作選擇。html
***********************************************************************************************************************************************************node
最初開始禁用 C++ STL,更多地是早期項目編碼實踐中留下的慣例,被後來的程序員繼承下來。老項目中這種選擇尤爲地多。不過若是有人將其上升到公司行爲在不一樣項目中全面禁用 STL,則沒有必要,並且我傾向於作這種決定的人並不理解 C++ 編譯系統。
通常來講,項目中禁用 C++ 多見於兩種具體場景:或者項目的產出產品爲函數庫,或者須要引用第三方函數庫。具體地來講,有三個主要緣由:
第一個緣由是二進制邊界混亂。對須要在項目中使用第三方函數庫的程序員來講,二進制邊界是個頭痛的問題。C++ 在這一方面自己就處理得不算好,加上模板後起到的是雪上加霜的後果。沒有經驗的程序員會貪圖方便而在公開頭文件中使用 C++ 模板,若是這時調用方的編譯器選項設置或 STL 版本和編譯方不一樣,那麼就可能出現一樣的頭文件在不一樣的環境下二進制佈局不符的狀況。——順便說一句,在過去十年裏,各個主流編譯器附帶的 STL 版本變化節奏不慢,因此這種因爲編譯環境不一樣而致使的 bug 並不算罕見,但缺少彙編知識的用戶難以排查。
第二個緣由是不肯使用異常。現在除了 Android 上的 STLPort 關閉異常,大部分主流 C++ STL 實現裏都沒法脫離異常使用 STL。異常帶來的問題主要是兩個:性能降低,代碼膨脹。這幾年 C++ 編譯器在性能方面的改進不少,good path 的性能問題已經基本沒有,但代碼膨脹問題卻沒有太多改善,甚至這個性能問題的一部分解決方案就是以代碼膨脹爲代價。我寫過一篇短文比對過 Android 上 gcc 4.6 在有無異常的狀況下的彙編代碼邏輯,能夠看到,啓動異常時生成的彙編代碼量多出了至關一部分(個人例子中是 50%),用於處理各類隱含代碼中的異常問題。這一條在手機系統中有時候會引發意想不到的麻煩,好比軟件升級後致使 app 在低存儲容量的手機中安裝失敗。順便說一句,這個問題並非 gcc 獨有,clang 上生成的代碼是同樣的。參考:http://dummydigit.net/posts/2014-01-01-23-30-1.html。
最後一個緣由是 C 兼容。嚴格地說,STL 在這個問題上算是躺槍,這個坑在不少具體的場景中也是由於異常而引入,但這個問題的麻煩程度比前兩個問題更高。好比 gcc 在編譯純 C 代碼時默認關閉 -fexceptions 選項,所以這樣編譯出來的代碼中沒有異常處理相關的棧展開。若是某個 C++ 項目引用了一個第三方 C 項目,它很難確保那個 C 項目給出的二進制代碼中正確進行了異常處理並保證代碼服從異常安全操做。這種場景下混用 C/C++ ,就可能在拋出異常時莫名其妙地崩潰或者出現 C 代碼區段中的資源泄漏,特別是 expat 那種大量利用回調的代碼結構。要規避這種風險並不是不可能,但須要 C 的架構部分作修改,好比使用 DOM 那種樹形結構,這種作法對歷史項目而言又很難辦到。換言之,若是一個項目出於種種緣由須要保持 C 兼容,而 STL 就屬於其中一個不可控的變數,與其相信程序員不犯錯,不如直接禁用更可控一些。參考:Code Gen Options
要解決二進制相關的問題很簡單:整個項目的全部相關代碼在同一個代碼基上編譯,強制打開編譯選項添加異常代碼,並去除一切二進制依賴。但對不少小公司來講,引入這樣的系統對配置管理的要求較高。若是一部分依賴關係來自本身並不瞭解的第三方代碼,輕易修改編譯選項可能帶來的風險與第三方代碼庫的規模成正比。退一步說,即使團隊裏真的有強大的配置管理工程師可以搞定一切,他們也不會有能力解決代碼膨脹問題,除非他們有權決定換一個編譯器。相比之下,前面朋友所說的所謂性能或者編譯出錯時糟糕的可讀性,在我看來反卻是次要因素,並且這些缺陷都正在新的編譯器中逐步獲得解決或改善,好比 clang。
因此那些選擇禁用 STL 的早期項目負責人,總有一些確實的理由,沒有人那麼彆扭地想跟開發效率過不去。至於後來的人是真的仔細想過這些細節仍是人云亦云,那是另外一個問題。c++
****************************************************************************************************************************************************************git
一些廣泛解決方案和回答:程序員
最安全的方式是不要跨 DLL boundary 使用 C++ 對象,尤爲是 STL。
DLL 和調用 DLL 程序使用的是否是一個堆,取決於整個工程的 CRT 使用方式。若是 CRT 是動態連接的,那麼程序自己和全部 DLL 都使用一個堆。若是 CRT 是靜態連接,那麼必然是逐一靜態連接到程序自己和每一個 DLL 上,那麼每一個模塊都是一個單獨的堆。小程序
c++ - How can I use Standard Library (STL) classes in my dll interface or ABI?
一個原則是,你的DLL公共類的ABI必須是肯定的,除非你本身改代碼,不然這個ABI不該該隨着DLL的編譯器版本而改變,可是STL作不到這一點,由於他的實現不受DLL做者控制。可是STL能夠在DLL私有部分使用,只要其細節不被暴露出來便可。
推薦文章:"How to export an instantiation of a Standard Template Library (STL) class and a class that contains a data member that is an STL object「 - 微軟數組
能夠,要注意一些細節,先佔坑
已填坑
爪機碼字一大段直接沒了,個人心好痛
1.exe和dll所有使用動態連接
由於dll和exe是連接到同一個運行庫dll上的,所以使用的是同一份運行庫的代碼,內存分配是在一個堆上的。對於全部類全部特性都能跨二進制文件使用
2.只要任意一個使用了靜態連接,你就不能向上面同樣使用
緣由:靜態連接會在生成的二進制文件裏包含庫的代碼,那麼你的dll和exe使用的實際上是兩個運行庫的代碼,天然會有兩個不一樣堆的問題
解決方案:
1.對於dll中導出的對象避開所
有malloc(new)和free(delete)操做,dll中使用exe裏的對象也同樣
2.將exe裏和dll裏的malloc(new)和free(delete)地址弄出來,人工避免混用
3.利用c++虛函數。因爲虛函數的地址是在運行期綁定的,所以你能夠利用這個特性來避免混用exe和dll中的malloc(new)和free(delete)。好比你在dll裏new了一個class a並在exe裏使用它,那你就不能在exe裏直接delete之。你應該爲a添加一個virtual的release
virtual void release()
{
delete this;
}
須要釋放資源的時候調用release。對於全部須要申請內存的操做同理。
然而對於string在靜態連接的狀況下除了解決方法1都沒有什麼卵用,除非你本身實現一個。安全
references:架構
http://www.zhihu.com/question/20201972app
http://www.zhihu.com/question/32127579