string类模拟实现
std::string 类作为 C++ 标准库中非常重要的一个类型,它封装了字符串的动态分配、内存管理以及其他字符串操作。
基本构思与设计
一个简化版的 string 类需要满足以下基本功能:
- 存储一个字符数组(
char*)。 - 记录字符串的长度和容量。
- 实现一些常见的字符串操作:如拷贝构造、赋值、拼接、访问字符等。
- 动态扩展数组以支持变长字符串。
基础成员变量和构造函数
首先,我们定义一个 String 类的基本结构。为了模拟 std::string,我们需要存储字符数组、字符串的大小以及容量。
class String
{
public:
// ...
private:
char* data;
size_t size;
size_t capacity;
};
其中data是字符数组,size表示已存储字符个数,capacity表示能存储字符个数(不包括"\0")
// 默认构造函数
String()
:data(new char[1])
{
data[0] = '\0';
size = capacity = 0;
}
// 构造函数:根据 C 字符串初始化
String(const char* str)
{
size = strlen(str);
capacity = size;
data = new char[capacity + 1]; // 多一个空间用于存放'\0'
strcpy(data, str);
}
两个构造函数还可以合二为一采用缺省值写法。
String::String(const char* str = "")
{
size = strlen(str);
capacity = size;
data = new char[capacity + 1];
strcpy(data, str);
}
特别注意:此处的缺省值只能给str = ""表示一个空串。若给其他值会导致在构造空串时不合理。
拷贝构造函数和赋值
// 拷贝构造函数
String(const String& rhs)
{
size = rhs.size;
capacity = rhs.capacity;
data = new char[capacity + 1];
strcpy(data, rhs.data);
}
// 赋值操作符重载
String& operator=(const String& rhs)
{
// 检查自赋值
if (this != &rhs)
{
delete[] data; // 释放原有的内存资源
size = rhs.size;
capacity = rhs.capacity;
data = new char[capacity + 1];
strcpy(data, rhs.data);
}
return *this;
}
特别注意:赋值操作符重载时不要忘记释放原有的内存资源
reserve函数
// 重新分配内存
void String::reserve(size_t new_capacity)
{
// 如果新的容量小于当前容量,不做任何事情
if (new_capacity > capacity) {
char* new_data = new char[new_capacity + 1];
strcpy(new_data, data);
delete[] data;// 释放原有的内存资源
data = new_data;
capacity = new_capacity;
}
}
字符串拼接相关函数
// 追加字符
void push_back(char ch)
{
if (size == capacity)
{
// 重新分配内存
// 先判断capacity是否为0,如果为0,新的容量为2,否则为原来的2倍
size_t new_capacity = capacity == 0 ? 2 : 2 * capacity;
reserve(new_capacity);
}
data[size++] = ch;
data[size] = '\0'; // 添加字符串结束符
}
// 追加字符串
void append(const char* str)
{
size_t new_size = size + strlen(str);
if (new_size > capacity)
{
// 重新分配内存
size_t new_capacity = capacity == 0 ? 2 : 2 * capacity;
// 扩容到足够容纳新字符串的大小,以2倍数扩容
while (new_capacity < new_size)
{
new_capacity *= 2;
}
reserve(new_capacity);
}
strcat(data, str);
size = new_size;
}
重载+=运算符
// 重载+=运算符
String& String::operator+=(char ch)
{
push_back(ch);
return *this;
}
String& String::operator+=(const char* str)
{
append(str);
return *this;
}
直接复用上面的代码
insert函数
String& insert(size_t pos, char ch)
{
assert(pos <= size);
if (size == capacity)
{
// 重新分配内存
size_t new_capacity = capacity == 0 ? 2 : 2 * capacity;
reserve(new_capacity);
}
// 将pos位置及其后面的字符向后移动一个位置
for (size_t i = size; i > pos; --i)
{
data[i] = data[i - 1];
}
data[pos] = ch;
++size;
return *this;
}
String& insert(size_t pos, const char* str)
{
assert(pos <= size);
size_t len = strlen(str);
size_t new_size = size + len;
if (new_size > capacity)
{
// 重新分配内存
size_t new_capacity = capacity == 0 ? 2 : 2 * capacity;
// 扩容到足够容纳新字符串的大小,以2倍数扩容
while (new_capacity < new_size)
{
new_capacity *= 2;
}
reserve(new_capacity);
}
// 将pos位置及其后面的字符向后移动len个位置
for (size_t i = size; i >= pos; --i)
{
data[i + len] = data[i];
}
// 将str插入到pos位置
for (size_t i = 0; i < len; ++i)
{
data[pos + i] = str[i];
}
size = new_size;
return *this;
}
此处需要注意挪动数据的方式。
erase函数
// 删除函数
String& erase(size_t pos, size_t len = npos)
{
assert(pos < size);
// 如果len超过pos到字符串末尾的长度,只删除pos到字符串末尾的长度
if (len > size - pos) {
data[pos] = '\0';
size = pos;
}
else {
// 将pos+len位置及其后面的字符向前移动len个位置
size_t i = pos + len;
while (i <= size)
{
data[pos++] = data[i++];
}
size -= len;
}
return *this;
}
其中npos是一个常量
const static size_t npos = -1;
resize函数
// 调整字符串大小
void resize(size_t new_size, char ch = '\0')
{
if (new_size > size)
{
if (new_size > capacity)
{
// 重新分配内存
reserve(new_size);
}
// 将ch添加到字符串末尾
for (size_t i = size; i < new_size; ++i)
{
data[i] = ch;
}
}
data[new_size] = '\0'; // 添加字符串结束符
size = new_size;
}
查找函数
size_t find(char ch, size_t pos) const
{
for (size_t i = pos; i < size; ++i) {
if (data[i] == ch) {
return i;
}
}
return npos;
}
size_t find(const char* str, size_t pos) const
{
char* p = strstr(data + pos, str);
return p ? p - data : npos;
}
输入输出
istream& operator>>(istream& is, String& str)
{
char ch;
ch = is.get();
while (ch != ' ' && ch != '\n') {
str.push_back(ch);
ch = is.get();// 读取下一个字符,可以读取空格
}
return is;
}
istream& getline(istream& is, String& str)
{
char ch;
ch = is.get();
while (ch != '\n') {
str.push_back(ch);
ch = is.get();// 读取下一个字符,可以读取空格
}
return is;
}
ostream& operator<<(ostream& os, const String& str)
{
os << str.c_str();
return os;
}
比较运算符重载
bool operator==(const String& lhs, const String& rhs)
{
int ret = strcmp(lhs.c_str(), rhs.c_str());
return ret == 0;
}
bool operator!=(const String& lhs, const String& rhs)
{
return !(lhs == rhs);
}
bool operator<(const String& lhs, const String& rhs)
{
int ret = strcmp(lhs.c_str(), rhs.c_str());
return ret < 0;
}
bool operator>(const String& lhs, const String& rhs)
{
int ret = strcmp(lhs.c_str(), rhs.c_str());
return ret > 0;
}
bool operator<=(const String& lhs, const String& rhs)
{
return !(lhs > rhs);
}
bool operator>=(const String& lhs, const String& rhs)
{
return !(lhs < rhs);
}
重载[]运算符
char& operator[](size_t i)
{
return data[i];
}
const char& operator[](size_t i) const
{
return data[i];
}
迭代器
iterator begin()
{
return data;
}
const_iterator begin() const
{
return data;
}
iterator end()
{
return data + size;
}
const_iterator end() const
{
return data + size;
}
其中iterator来自typedef
typedef char* iterator;
typedef const char* const_iterator;
大小相关
size_t Size() const
{
return size;
}
size_t length() const
{
return size;
}
size_t Capacity() const
{
return capacity;
}
完整代码
GitHub链接



















