侃一侃vc的std string

2021-06-04 00:50:27 字數 4053 閱讀 1135

那天心血來潮敲下了這坨**:

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 下文即摘自該書。埃瑞克 挪威 口述,郭瑩撰稿。西方無論從事什麼職業都無高低貴賤之分,強調的是幹事業的興趣和自在愉快。而自己人生價...