前言:python
說是漫遊,其實就是扯,一點一點的扯。ios
話說以前參加華爲的德州撲克比賽,我用C++解析消息的時候碰到一個小問題,就是定長收消息的時候出錯,在Linux下調了好久好久,終於發現,sizeof(string)不是string的size,而是string類型的大小。固然,用string.size()就能夠輕鬆解決了,而做品也在昨晚提交了,不過,交的是python的。個人C++程序,送給了兩支隊伍,讓他們去參賽,惋惜,白眼狼。windows
既然有閒暇時間了,那麼就要深究一下,sizeof(string)是個什麼鬼,我從代碼測試開始,查閱到容器的幾個屬性的不一樣,再定位到了string的幾種實現,終於明白了sizeo(string)f的大小,又對引用計數產生量興趣。下文將按照這個路線,簡單的闡述一下過程與實現。函數
1、sizeof(string)的兩個值測試
先上測試代碼:spa
#include <string> #include <iostream> int main() { std::string first = "abcde"; std::string second = first; std::string c; std::cout << "sizeof result of each str " << std::endl; std::cout << "first:" << sizeof(first) << std::endl; std::cout << "second:" << sizeof(second) << std::endl; std::cout << "c:" << sizeof(c) << std::endl; return 0; }
本代碼是測試三種不一樣形式的string的大小,運行結果以下:3d
全是28,是char*的7倍。此結果是在windows下,vs2013跑的結果。而在Linux下,g++4.6的結果是4。指針
那麼問題來了爲何first,second,c的sizeof都是同樣大小,而又爲何在不一樣編譯器下,值不一樣呢?code
首先,回答爲何first,second,c的sizeof都是同樣大小。對象
一、什麼是sizeof。
sizeof是一個關鍵字,返回對象在內存中的大小,例如sizeof(char*) = 4. 那麼sizeof(string)返回的固然是string這個變量在內存(棧)中的大小了。不過爲何對於不一樣string,在同一平臺下返回的值都是相同的。
二、string在vs2013中實現的猜想。
首先,string是一種容器,那麼它有這容器的函數size()與capacity(),其中,size()是容器對象實際存儲的對象個數,而capacity()返回的是容器對象總共能夠存儲的值。那麼,既然sizeof(string)的返回值是char * 的7倍,那麼是否是value存在一個動態內存中,而size(),capacity()等在一塊呢,加上點什麼,而後湊夠了7倍的char *?
通過查閱資料發現,有一種string的實現是這樣的:string對象指針大小的7倍,包含:一個分配子,默認大小爲15的存實際字符的value,一個size(),一個capacity(),總共:4+15+1+4+4 = 28;其中1表明字符串預留的''0',方便c_str()的實現.正好是char*的7倍。示意圖以下:
也就是說,當字符串的有效字符少於15時,建立一個新的對象,不會致使動態內存分配,那麼當字符串長度大於15呢?則在Value部分擠出一塊做爲指向動態內存的指針,動態內存中存實際的字符,示意圖以下:
經過對有效字符小於15的string調用capacity()發現,確實是15,而大於15的,則爲會大於等於實際字符,這個與容器的內存分配策略有關。屬於預分配空間的一種。至此,解決了,爲何first,second,c的sizeof返回值在同一平臺下同樣的問題。
其次,解釋爲何g++與vs2013下結果不一樣。
緣由很簡單了,string在不一樣平臺下可能存在不一樣的實現。既然Linux的g++下返回值是4,那麼猜想它就一個指針,而後指針指向了具體的結構。通過查資料發現,至少有兩種string的實現,在sizeof下都會返回4.
一、第一種可能的實現
這種實現是一個指針指向一個結構,而後這個結構一個地方指向有效字符,示意圖以下:
Other是一些與同步相關的數據。建立該實現的對象時,至少會致使兩次內存動態分配。
二、第二種可能的實現
該實現比上一種直接一些,示意圖以下:
建立該對象時,只會引起一次動態內存分配。
2、「拖延症」與引用計數
細心的讀者可能發如今最後兩種視線中有個RefCnt。這個是引用計數,那麼它是作什麼用的呢?
一、寫時複製
在後兩種實現中,你當用出以下語句時 :
string s = s1;
s 與 s1只是pointer不一樣,其他的都是共享的,這樣能夠儘可能減小構造函數與析構函數的調用。當其中一個發生變化時,被寫入新值時,纔會真正的建立一個實實在在的對象。Linux中的fwrite等IO都拖延症的思想,fork也是如此,這樣儘可能減小內存分配,構造與析構。
二、引用計數
因爲多個指針共享同一動態內存,只有當RefCnt = 0時,纔會析構內存,這樣保證每一個共享的字符串都是能正常工做的。Linux中文件描述符就是這樣的。
參考:
Effective STL