string
- 手寫C++字符串類
- 類的基本結構與成員變量
- 一、構造函數與析構函數
- 二、賦值運算符重載
- 三、迭代器支持
- 四、內存管理與擴容機制
- 五、字符串操作函數
- 六、運算符重載
- 總結

手寫C++字符串類
從零實現一個簡易版std::string
類的基本結構與成員變量
namespace zzh {
class string {
private:char* _str; // 存儲字符串的字符數組size_t _size; // 當前字符串長度size_t _capacity; // 已分配的容量
public:static const size_t npos; // 表示"不存在的位置"// 各類成員函數...
};
}
這個自定義字符串類主要通過動態分配的字符數組_str
來存儲字符串內容,并維護兩個重要狀態:_size
表示當前字符串長度,_capacity
表示已分配的內存容量。
- 基本成員變量
在自定義 string 類中,我們需要定義一些基本的成員變量來存儲字符串的內容和相關信息:
_str:用于存儲字符串的字符數組,通常是一個動態分配的 char 類型數組。
_size:表示當前字符串的實際長度,不包括結尾的空字符 \0。
_capacity:表示分配的內存容量,通常大于或等于 _size,用于優化內存分配效率。 - 構造函數和析構函數
構造函數用于初始化 string 對象,常見的構造方式包括:
從 C 風格字符串構造(const char*):通過 strlen 計算字符串長度,并動態分配內存來存儲字符串內容。
拷貝構造函數:用于從另一個 string 對象構造新對象,需要深拷貝內存以避免懸掛指針問題。
默認構造函數:用于創建一個空字符串。
析構函數則負責釋放動態分配的內存,避免內存泄漏。 - 賦值運算符重載
為了支持對象之間的賦值操作,我們需要重載賦值運算符 =。在實現時,需要注意自賦值的情況,并進行深拷貝以確保兩個對象的內存獨立。 - 內存管理
字符串操作中,內存管理是一個關鍵問題。我們需要在字符串長度超過當前容量時動態擴展內存。通常的做法是將容量加倍,以減少頻繁的內存分配操作。 - 迭代器支持
為了方便遍歷字符串中的字符,我們可以提供迭代器支持。通過定義 begin() 和 end() 方法,返回指向字符串首尾的指針,可以方便地使用標準庫算法。 - 常見操作實現
追加字符或字符串:通過 push_back 和 append 方法,可以在字符串末尾添加字符或另一個字符串的內容。在實現時,需要注意內存容量是否足夠,并在必要時進行擴展。
查找和替換:提供 find 方法用于查找字符或子字符串的位置,insert 和 erase 方法用于插入和刪除字符或子字符串。
比較操作:重載比較運算符(如 <、>、== 等),以便可以直接比較兩個 string 對象的大小。
一、構造函數與析構函數
// 1. 從C風格字符串構造
string::string(const char* str)
{_size = strlen(str);_str = new char[_size + 1];_capacity = _size;memcpy(_str, str, _size + 1);
}// 2. 拷貝構造函數
string::string(const string& s)
{_size = s._size;_capacity = s._capacity;_str = new char[_capacity + 1];memcpy(_str, s._str, _size + 1);
}// 3. 析構函數
string::~string()
{delete[] _str;_str = nullptr;_size = 0;_capacity = 0;
}
關鍵點:
- 構造函數負責分配內存并復制字符串內容
- 拷貝構造函數實現深拷貝,避免內存共享
- 析構函數必須釋放動態分配的內存,防止內存泄漏
二、賦值運算符重載
string& string::operator=(const string& s)
{if (this != &s){char* tmp = new char[s._capacity + 1];memcpy(tmp, s._str, s._size + 1);delete[] _str; // 注意:原代碼此處順序有誤,已修正_str = tmp;_size = s._size;_capacity = s._capacity;}return *this;
}
技術要點:
- 使用臨時變量確保異常安全
- 自我賦值檢查避免無效操作
- 先分配新內存再釋放舊內存,防止內存泄漏
三、迭代器支持
string::iterator string::begin() { return _str; }
string::iterator string::end() { return _str + _size; }
說明:
- 迭代器本質是字符指針
begin()
返回字符串首地址end()
返回字符串末尾的下一個位置
四、內存管理與擴容機制
// 預分配內存
void string::reserve(size_t n)
{if (n > _capacity){char* tmp = new char[n + 1];memcpy(tmp, _str, _size + 1);delete[] _str;_str = tmp;_capacity = n;}
}// 追加字符
void string::push_back(char c)
{if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(newcapacity);}_str[_size++] = c; // 注意:原代碼此處錯誤地寫入了固定字符'c'_str[_size] = '\0';
}
內存管理策略:
- 采用指數級擴容(2倍)減少內存分配次數
reserve()
實現預分配,避免頻繁擴容- 每次擴容后保留額外空間,提高插入效率
五、字符串操作函數
// 追加C風格字符串
void string::append(const char* str)
{size_t len = strlen(str);if (_size + len > _capacity){size_t newcapacity = 2 * _capacity > _size + len ? 2 * _capacity : _size + len;reserve(newcapacity);}memcpy(_str + _size, str, len); // 注意:原代碼此處多復制了一個終止符_size += len;_str[_size] = '\0'; // 手動添加終止符
}// 查找字符
size_t string::find(char c, size_t pos = 0) const
{for (size_t i = pos; i < _size; i++){if (_str[i] == c)return i;}return npos;
}// 插入字符
string& string::insert(size_t pos, char c)
{assert(pos <= _size);if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(newcapacity);}// 從后向前移動元素for (size_t i = _size; i > pos; i--)_str[i] = _str[i - 1];_str[pos] = c;_size++;return *this;
}
核心算法:
append()
通過內存拷貝實現高效追加find()
線性查找目標字符insert()
通過元素后移實現插入操作- 使用
memmove()
處理內存重疊情況
六、運算符重載
// 比較運算符
bool string::operator<(const string& s)
{size_t i1 = 0, i2 = 0;while (i1 < _size && i2 < s._size){if (_str[i1] < s._str[i2])return true;else if (_str[i1] > s._str[i2])return false;i1++; i2++;}return i1 < s._size; // 注意:原代碼此處邏輯有誤,已修正
}// 索引運算符
char& string::operator[](size_t index)
{assert(index < _size);return _str[index];
}
實現要點:
- 比較運算符按字典序逐字符比較
- 索引運算符提供隨機訪問能力
- 提供常量和非常量兩個版本的重載
總結
通過手寫這個簡易版string
類,我們深入理解了標準庫字符串類的核心機制:動態內存管理、深拷貝實現、迭代器設計、擴容策略等。雖然現代C++編程中應優先使用std::string
,但掌握這些底層原理有助于寫出更高效、更安全的代碼。