string
- 前言
- 核心成员变量设计
- 构造函数与析构函数
- 默认构造函数
- 从C风格字符串构造
- 填充构造
- 拷贝构造函数
- 迭代器范围构造
- 析构函数
- 基本操作实现
- 迭代器支持
- 容量管理
- 元素访问
- 字符串修改操作
- 拼接操作
- 插入与删除
- 字符串查找操作
- 运算符重载
- 总结
- 每文推荐
前言
在C++中,std::string
是我们日常开发中频繁使用的字符串类,它封装了复杂的内存管理和字符串操作细节。今天我们来手动模拟实现一个简化版的string类,深入理解其底层工作原理。
核心成员变量设计
一个基础的string类需要包含三个核心成员变量:
private:char* _a = new char[1] {'\0'}; // 存储字符串的字符数组size_t _size = 0; // 当前字符串长度size_t _capacity = 0; // 当前容量(可容纳的最大字符数,不含终止符)
_a
:指向动态分配的字符数组,用于存储字符串内容_size
:记录当前字符串中有效字符的数量_capacity
:记录当前分配的内存可容纳的最大字符数(不包括末尾的’\0’)
构造函数与析构函数
默认构造函数
默认构造一个空字符串:
string::string():_size(0), _a(new char[1] {'\0'}), _capacity(0)
{}
从C风格字符串构造
通过const char*
构造string,需要先计算字符串长度,再分配内存并复制内容:
string::string(const char* s)
{size_t n = strlen(s);reserve(n); // 预留足够空间*this += s; // 使用+=操作符复制内容
}
填充构造
创建包含n个字符c的字符串:
string::string(size_t n, char c)
{reserve(n);while (n--){*this += c;}
}
拷贝构造函数
实现深拷贝,避免浅拷贝导致的内存问题:
string::string(const string& str)
{string tmp = str._a; // 利用已有的构造函数swap(tmp); // 交换当前对象和临时对象的资源
}
迭代器范围构造
通过迭代器范围构造字符串:
template <class InputIterator>
string::string(InputIterator first, InputIterator last)
{reserve(last - first); // 预留足够空间while (first != last){*this += *first;first++;}
}
析构函数
释放动态分配的内存:
string::~string()
{delete[] _a;_a = nullptr;_size = 0;_capacity = 0;
}
基本操作实现
迭代器支持
实现begin和end迭代器,方便使用范围for循环:
iterator begin() { return _a; }
const_iterator begin() const { return _a; }
iterator end() { return _a + _size; }
const_iterator end() const { return _a + _size; }
容量管理
reserve
函数用于预留内存空间,避免频繁的内存分配:
void string::reserve(size_t n)
{if (_capacity <= n){_capacity = n;char* tmp = new char[_capacity + 1] {}; // +1 用于存储终止符'\0'memcpy(tmp, _a, _size);delete[] _a;_a = tmp;}
}
resize
函数用于调整字符串长度:
void string::resize(size_t n)
{if (_capacity < n){char* tmp = new char[n + 1] {};memcpy(tmp, _a, _size);delete[] _a;_a = tmp;_capacity = n;}_size = n;_a[_size] = '\0';
}// 带填充字符的重载版本
void string::resize(size_t n, char c)
{// 实现略...
}
元素访问
重载[]
操作符实现元素访问:
char& operator[](size_t pos)
{assert(_size > pos); // 边界检查return _a[pos];
}const char& operator[](size_t pos) const
{assert(_size > pos);return _a[pos];
}
字符串修改操作
拼接操作
实现+=
操作符和append
方法:
string& string::operator+=(const string& str)
{for (size_t i = 0; i < str._size; i++){if (_size == _capacity){// 容量不足时扩容,翻倍增长_capacity = _capacity == 0 ? 4 : _capacity * 2;reserve(_capacity);}_a[_size] = str[i];_size++;}_a[_size] = '\0';return *this;
}// 其他重载版本:+= const char*、+= char
// append方法通过调用+=实现
string& string::append(const string& str)
{*this += str;return *this;
}
插入与删除
实现insert
和erase
方法:
string& string::insert(size_t pos, const string& str)
{assert(pos <= _size);if (_size + str.size() > _capacity){reserve(_size + str.size()); // 确保有足够空间}// 移动现有字符为新内容腾出空间memmove(_a + pos + str._size, _a + pos, _size - pos);// 复制新内容for (size_t i = 0; i < str._size; i++){_a[pos + i] = str[i];}_size += str.size();return *this;
}string& string::erase(size_t pos, size_t len)
{assert(pos < _size);if (len > _size || pos + len > _size){// 删除到末尾_a[pos] = '\0';_size = pos;return *this;}// 移动后面的字符覆盖被删除的部分while (pos + len < _size){_a[pos] = _a[pos + len];pos++;}_size -= len;_a[_size] = '\0';return *this;
}
字符串查找操作
实现find
和find_first_of
系列方法:
size_t string::find(const string& str, size_t pos) const
{for (size_t i = pos; i < _size; i++){if (_a[i] == str[0]){size_t j = 1;for (j = 1; j < str._size && (i + j) < _size; j++){if (_a[i + j] != str[j]){break;}}if (j == str._size){return i; // 找到匹配,返回起始位置}}}return npos; // 未找到,返回npos
}
运算符重载
重载比较运算符:
bool operator== (const string& lhs, const string& rhs)
{if (strcmp(lhs._a, rhs._a) == 0){return true;}return false;
}bool operator> (const string& lhs, const string& rhs)
{if (strcmp(lhs._a, rhs._a) > 0){return true;}return false;
}// 其他比较运算符(!=, >=, <, <=)可通过上述运算符组合实现
重载输入输出运算符:
ostream& operator<< (ostream& os, const string& str)
{for (size_t i = 0; i < str.size(); i++){os << str[i];}return os;
}istream& operator>> (istream& is, string& str)
{str.clear();char buffer[64] = { 0 };int i = 0;char c = is.get();while (c != ' ' && c != '\n') // 遇到空格或换行符停止{buffer[i++] = c;if (i == 63) // 缓冲区满,先存入string{buffer[i] = '\0';str.reserve(str.capacity() + 64);str += buffer;i = 0;}c = is.get();}if (i != 0) // 处理剩余字符{buffer[i] = '\0';str += buffer;}return is;
}
总结
通过手动实现string类,我们深入理解了动态内存管理、字符串操作的底层实现以及类设计的基本原理。这个简化版的string包含了标准库string的核心功能,包括构造/析构、容量管理、元素访问、字符串修改和查找等操作。
实际的std::string
实现会更加复杂,还会考虑异常安全性、小字符串优化(SSO)、多线程安全等问题,但核心思想是一致的。理解这些底层实现有助于我们更好地使用标准库,写出更高效的代码。
如需源码,可在我的gitee上找到,下面是链接:
string
如对您有所帮助,可以来个三连,感谢大家的支持。
每文推荐
Aimer–Ref:rain
张碧晨–光的方向
张杰–追风赶月的人
学技术学累了时可以听歌放松一下。