某日二師兄參加XXX科技公司的C++工程師開發崗位第18面: > 面試官:`std::string`用過吧? > > 二師兄:當然用過(廢話,C++程式員就沒有沒用過`std::string`的)。 > > 面試官:`std::string("hello")+"world"`、`"hello"+st ...
某日二師兄參加XXX科技公司的C++工程師開發崗位第18面:
面試官:
std::string
用過吧?二師兄:當然用過(廢話,C++程式員就沒有沒用過
std::string
的)。面試官:
std::string("hello")+"world"
、"hello"+std::string("world")
和std::string("hello")+std::string("world")
的結果是什麼?為什麼?二師兄:前者和後者的結果都是
std::string
的對象,內容是“helloworld\0
”,而中間的這個表達式無法通過編譯。原因是std::string
重載了operator+(const char*)
和operator+(const std::string&)
,但是const char*
卻沒有重載operator+
運算符。面試官:
std::string
有兩個API
,resize
和reserve
,你知道它們之間的區別嗎?二師兄:
resize
對應的是size
,resize
可以改變字元串的大小。reserve
對應的是capacity
,reserve
只能改變capacity
的大小。二師兄:當
resize
傳入的參數小於字元串的szie
時,多餘的字元串會被截取。當reserve
傳入的參數小於capacity
時,reserve
什麼也不會做。二師兄:當
resize
傳入的參數大於字元串的szie
時,增加的字元串會被預設初始化。當reserve
傳入的參數大於capacity
時,capacity
會被擴容。面試官:好的。可以通過下標訪問
std::string
實例的內容嗎?二師兄:可以的,
std::string
重載了下標運算符,可以像數組一樣通過下標運算取出某個字元。面試官:你知道
std::string
的at
成員方法嗎?二師兄: 嗯,和下標運算功能相似,不過不用擔心越界問題。可以安全的訪問字元串中的字元。
面試官:既然有
at
方法了,為什麼還要重載下標運算符呢?二師兄:主要是因為性能上的考量。
at
雖然保證了不會超出字元串範圍(超出範圍拋出異常),但是性能低於下標操作。這就是有舍有得。為了安全使用at
,為了性能使用下標操作。C++給了你多個選擇,如何選擇看你的需求。面試官:那你知道
std::string
是如何實現的嗎?二師兄:在
string
內部維護一個指針,這個指針指向真正的字元串的位置。面試官:能簡單的寫一下實現代碼嗎?
二師兄:好的。
class string
{
public:
string():size_(0),data_(nullptr){}
explicit string(const char* c)
{
size_ = strlen(c);
data_ = (char*)malloc(size_+1);
memset(data_,0,size_+1);
memcpy(data_,c,size_);
}
size_t size() const {return size_;}
const char* c_str() const {return data_;}
private:
size_t size_;
char* data_;
};
二師兄:在實現
append
或者+=
的時候,需要把當前字元的長度加上append
的內容的長度,以此長度申請一塊新記憶體,然後把當前字元串的記憶體和append
的內容考入新申請的記憶體中。free
掉之前data_
指向的記憶體,然後把data_
指針指向新申請的記憶體。面試官:好的。這樣的實現有一些弊端。如果頻繁的對一個
std::string
對象append
內容,會發生什麼?二師兄:是的,因為頻繁的
malloc
和free
,會有性能問題。因所以編譯器在實現std::string
的時候一般會預先申請一塊大的記憶體,這塊記憶體的長度是capacity
,當添加的字元串的長度加上當前的字元串長度小於capacity
時,直接添加到當前的塊上即可。面試官:好的。針對字元串比較少的情況,一般編譯器會做一些優化,你知道如何優化的嗎?
二師兄:這個好像在哪看過,不記得額。。。
面試官:好的,今天的面試結束了,請回去等通知吧。
今天二師兄的表現不錯,除了最後一個問題,基本上都答上來了。讓我們來看下這個問題:
針對字元串比較少的情況,一般編譯器會做一些優化,你知道如何優化的嗎?
我們可以看看GCC中std::string
的實現:
typedef basic_string<char> string;
_Alloc_hider _M_dataplus;
size_type _M_string_length;
enum { _S_local_capacity = 15 / sizeof(_CharT) };
union
{
_CharT _M_local_buf[_S_local_capacity + 1];
size_type _M_allocated_capacity;
};
這裡的_CharT
就是char
,所以_S_local_capacity
等於15
。當字元串的長度小於等於15
時,直接存在_M_local_buf
中,而不需要在堆中申請記憶體。當字元串長度大於15
時,在記憶體中申請一塊記憶體,這塊記憶體的起始地址保存在_M_dataplus
中,這塊記憶體的容量保存在_M_allocated_capacity
中,而字元串的真實長度保存在_M_string_length
中。當向字元串中添加字元時,如果添加字元的長度大於 _M_allocated_capacity - _M_string_length
,則需要resize
,否則直接追加到_M_dataplus
保存的記憶體塊中即可。
好了,今天的面試到這裡就結束了。感謝小伙伴們的耐心閱讀,咱們明天繼續二師兄的面試之旅!
關註我,帶你21天“精通”C++!(狗頭)