那天心血來潮敲下了這坨**:
cout
<<
sizeof
(std::string
) << endl;
我的平台是xp+vc9.0,執行結果是32,不知道為什麼要那麼多,於是在源**裡搗鼓了一番,許久之後終於有了一點眉目,下面是我的一些總結。
一,結構布局
string的原型是
typedef basic_string
<
char
, char_traits
<
char
>
, allocator
<
char
>
>
string;
它是basic_string乙個typedef,首先來看basic_string的類層次結構:
_container_base_secure /\
|_string_base /\
|_string_val /\
|basic_string
_container_base_secure裡有乙個_iterator_base型別的指標成員_myfirstiter,得佔4個位元組,_string_base沒有資料成員,_string_val裡有乙個_alval型別(乙個allocator)的成員_alty,沒有資料成員,只占用1個位元組,加上padding,也就是4個位元組,basic_string有3個資料成員,乙個union佔16個位元組,兩個跟長度相關的整型變數各4個位元組。所有類都不含虛函式,布局如下:
_container_base_secure
_myfirstiter:_iterator_base*(
4bytes)
_string_base (nop)
_string_val
_alty:_alval (
1byte+3
bytes padding)
basic_string
_bx : _bxty (
16bytes)
_mysize : size_type (
4bytes)
_myres : size_type (
4bytes)
這個是basic_string的union:
enum;
union _bxty
_bx;
通過這個union可以看出vc的std::string在字串長度較小的時候會使用乙個棧上緩衝區(_bxty::_buf)來儲存字串內容,如果字串長度超過了某個範圍則會使用allocator分配動態記憶體(_bxty::_ptr),_buf_size控制著這個範圍值,_buf緩衝區始終是16個位元組大小。可以看出,vc的std::string沒有使用通用copy-on-write技術,因為它沒有reference count成員。那麼vc採用的這個技術對於字串的使用效率在實際使用中表現如何呢?
下面是一段測試拷貝不同字串長度的**:
#include
<
windows.h
>
#include
<
stdio.h
>
#include
<
iostream
>
#include
<
string
>
#include
<
vector
>
using
namespace
std;
template
<
size_t n
>
void
test_string()
;memset(szbuf, 'a
', n -1
);// 計算拷貝時間
size_t time
=gettickcount();
for(
inti =0
; i
<
loop;
++i)
time
=gettickcount()
-time;
printf(
"buffer size: %u, loop: %u, used %u ms\n
", n, loop, time);
}int
main(
intargc,
char
*argv)
編譯使用release版全預設引數,結果如下:
buffer size: 4, loop: 1000000, used 78 ms
buffer size: 8, loop: 1000000, used 94 ms
buffer size: 12, loop: 1000000, used 93 ms
buffer size: 16, loop: 1000000, used 94 ms
buffer size: 20, loop: 1000000, used 422 ms
buffer size: 24, loop: 1000000, used 422 ms
buffer size: 32, loop: 1000000, used 438 ms
在字串(包含0結尾字元)小於16個位元組的時候消耗的時間都很低而且幾乎一致(94ms),而一旦超過了16個位元組消耗時間則迅速增加,變成了422ms(近5倍)。可以看出,如果使用長度較小的字串,在涉及大量拷貝處理的時候,vc這個技巧的效率還是挺高的。
下面是相同**採用使用了copy-on-write技術的gnu std::string的執行結果(ubuntu + gcc4.4 + -o2引數):
buffer size: 8, loop: 1000000, used 140 ms
buffer size: 12, loop: 1000000, used 119 ms
buffer size: 16, loop: 1000000, used 140 ms
buffer size: 20, loop: 1000000, used 126 ms
buffer size: 24, loop: 1000000, used 154 ms
buffer size: 32, loop: 1000000, used 129 ms
速度增長很平均,在16位元組以下的時候用時比vc多,但是超過16位元組後效率比vc高(細節沒去研究)。
二,虛析構函式
basic_string和它的基類_container_base_secure的析構函式都不是virutual function,這在繼承的時候會有***,先看下面這段**:
class
mystring :
public
std::
string
};void
main()
沒有虛函式所以析構函式無法動態繫結,上面的**執行到delete時將直接呼叫std::string的析構函式,而不會去找mystring的析構函式。如果~mystring涉及到資源釋放的話,那麼無疑上面的**將導致洩漏。
還有一點值得一提的就是對std::string或包含std::string的物件實施zeromemory
雖然大多數合格的c++程式設計師都知道不應該對非pod物件實施zeromomory,但是在工程實際中確實有這樣的**,而且他們執行的很正常(vc++下),這是為什麼呢?我試圖做乙個簡單解釋。
如上所述,std::string的繼承體系裡沒有virtual function,物件布局裡也就沒有vptr,而且整個繼承體系都採用的是單繼承,bptr又省了(參考lippman的),所以std::string物件的布局全是它的自身的資料成員,_container_base_secure的_myfirstiter是指標成員,本身在初始化時就需要置零,_string_val的_alval成員是個allocator,只有函式不包含資料,呼叫時也就不必傳this指標,所以給它置零不會有***,basic_string指標本身的三個成員分別是緩衝區,字串長度和預留長度,初始化時將它們置零也是沒有***的。所以對std::string進行zeromemory操作的**依然執行的很正常。
過年侃一侃
今年過年終於是第一次經歷春運,也才感受到春運回家不易,年前乙個月就開始搶票,各種找黃牛,12306bypass按時按點搶票。終於是有買到來回的票,年前請假,年後請假,也是想要在家多待一段時間。回家走親訪友,也終於是意識到自己已經24了,從22到24怎麼這麼快,一下就感覺自己變老了。和朋友聚會,避免不...
辣侃情戀男女的犀利段子
開心一刻笑話大王 1 愛情必須得有個人耍流氓開始。你不主動耍也就算了,還跟紀律委員似的。2 5歲時生病渾身難受讓你吃藥你偏不吃,15歲時早戀遇到壞男人讓你放手你偏不聽,25歲時到了待嫁年齡有人對你好你偏不要,大概有些女孩體內與生俱來一種性情,小時候叫任性,青春期叫叛逆,長大了叫作。3 最煩的就是有女...
換一雙眼睛看自己 老外侃中國
老外侃中國 作家出版社2003年8月出版 實錄。此實錄中,老外暢談他們在中國生活學習 工作 婚戀及與中國人交往的經歷經驗。該書可稱是國內外首部西方人集體談中國實錄。com 下文即摘自該書。埃瑞克 挪威 口述,郭瑩撰稿。西方無論從事什麼職業都無高低貴賤之分,強調的是幹事業的興趣和自在愉快。而自己人生價...