面試中STRING類的解答

C++ 的一個常見面試題是讓你實現一個 String 類,限於時間,不可能要求具備 std::string 的功能,但至少要求能正確管理資源。具體來說:

1、能像 int 類型那樣定義變量,並且支持賦值、複製。
2、能用作函數的參數類型及返回類型。
3、能用作標準庫容器的元素類型,即 vector/list/deque 的 value_type。(用作 std::map 的 key_type 是更進一步的要求,本文從略)。
換言之,你的 String 能讓以下代碼編譯運行通過,並且沒有內存方面的錯誤。

1 void foo(String x)
2 {
3 }
4 void bar(const String& x)
5 {
6 }
7 String baz()
8 {
9 String ret("world");
10 return ret;
11 }
12 int main()
13 {
14 String s0;
15 String s1("hello");
16 String s2(s0);
17 String s3 = s1;
18 s2 = s1;
19 foo(s1);
20 bar(s1);
21 foo("temporary");
22 bar("temporary");
23 String s4 = baz();
24 std::vector<String> svec;
25 svec.push_back(s0);
26 svec.push_back(s1);
27 svec.push_back(baz());
28 svec.push_back("good job");
29 }
本文給出我認爲適合面試的答案,強調正確性及易實現(白板上寫也不會錯),不強調效率。某種意義上可以說是以時間(運行快慢)換空間(代碼簡潔)。

首先選擇數據成員,最簡單的 String 只有一個 char 成員變量。好處是容易實現,壞處是某些操作的複雜度較高(例如 size() 會是線性時間)。爲了面試時寫代碼不出錯,本文設計的 String 只有一個 char data成員。而且規定 invariant 如下:一個 valid 的 string 對象的 data 保證不爲 NULL,data_ 以 '\0' 結尾,以方便配合 C 語言的 str*() 系列函數。

其次決定支持哪些操作,構造、析構、拷貝構造、賦值這幾樣是肯定要有的(以前合稱 big three,現在叫 copy control)。如果鑽得深一點,C++11的移動構造和移動賦值也可以有。爲了突出重點,本文就不考慮 operator[] 之類的重載了。

這樣代碼基本上就定型了:

1 #include <utility>
2 #include <string.h>
3 class String
4 {
5 public:
6 String()
7 : data(new char[1])
8 {
9 *data
= '\0';
10 }
11 String(const char str)
12 : data(new char[strlen(str) + 1])
13 {
14 strcpy(data
, str);
15 }
16 String(const String& rhs)
17 : data(new char[rhs.size() + 1])
18 {
19 strcpy(data
, rhs.c_str());
20 }
21 /
Delegate constructor in C++11
22 String(const String& rhs)
23 : String(rhs.data)
24 {
25 }
26 */
27 ~String()
28 {
29 delete[] data
;
30 }
31 / Traditional:
32 String& operator=(const String& rhs)
33 {
34 String tmp(rhs);
35 swap(tmp);
36 return
this;
37 }
38 /
39 String& operator=(String rhs) // yes, pass-by-value
40 {
41 swap(rhs);
42 return
this;
43 }
44 // C++ 11
45 String(String&& rhs)
46 : data(rhs.data)
47 {
48 rhs.data_ = nullptr;
49 }
50 String& operator=(String&& rhs)
51 {
52 swap(rhs);
53 return this;
54 }
55 // Accessors
56 sizet size() const
57 {
58 return strlen(data
);
59 }
60 const char
cstr() const
61 {
62 return data
;
63 }
64 void swap(String& rhs)
65 {
66 std::swap(data, rhs.data);
67 }
68 private:
69 char* data_;
70 };
注意代碼的幾個要點:

只在構造函數裏調用 new char[],只在析構函數裏調用 delete[]。
賦值操作符采用了《C++編程規範》推薦的現代寫法。
每個函數都只有一兩行代碼,沒有條件判斷。
析構函數不必檢查 data_ 是否爲 NULL。
構造函數 String(const char* str) 沒有檢查 str 的合法性,這是一個永無止境的爭論話題。這裏在初始化列表裏就用到了 str,因此在函數體內用 assert() 是無意義的。
這恐怕是最簡潔的 String 實現了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章