从零搭建企业网站,宁波网站seo报价,室内设计女孩子学难吗,个人名下公司查询网. #x1f493; 博客主页#xff1a;倔强的石头的CSDN主页 #x1f4dd;Gitee主页#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏#xff1a;《C指南》 期待您的关注 文章目录 引言一、成员变量与内存管理1.1 核心成员变量1.2 内存分配策略 二、默认成员函数的实现与优化… . 博客主页倔强的石头的CSDN主页 Gitee主页倔强的石头的gitee主页 ⏩ 文章专栏《C指南》 期待您的关注 文章目录 引言一、成员变量与内存管理1.1 核心成员变量1.2 内存分配策略 二、默认成员函数的实现与优化2.1 拷贝构造函数2.2 赋值运算符重载2.3 析构函数 三、迭代器与元素访问3.1 迭代器实现3.2 运算符重载 四、容量管理4.1 reserve预分配内存4.2 resize调整字符串长度 五、修改操作5.1 清空字符串clear5.2 push_back与append5.3 insert与erase 六、其他关键函数实现6.1 查找函数find查找字符查找子串 6.2 子串生成substr6.3 流运算符重载流插入operator流提取operator 6.4 比较运算符重载等于与不等于大小比较 6.5 交换函数swap 七、性能优化与注意事项结语 引言 在前文中我们深入探讨了C标准库中basic_string的成员变量、默认成员函数及常用操作。 本文作为系列第三篇将结合模拟实现的代码逐行解析basic_string的底层原理涵盖构造函数、拷贝控制、容量管理、修改操作等核心功能的实现细节与优化技巧。 通过手写一个简化版string类帮助读者彻底理解std::string的内部工作机制。 一、成员变量与内存管理
1.1 核心成员变量
标准库的basic_string通过三个核心变量管理字符串
字符指针 _str指向动态分配的字符数组。当前长度 _size字符串有效字符个数不含\0。总容量 _capacity当前内存可容纳的最大字符数含\0。
模拟实现代码
namespace xc {
class string {
private:char* _str; // 字符存储指针size_t _size; // 有效字符数size_t _capacity; // 总容量含\0
public:static const size_t npos -1; // 特殊标记
};
}1.2 内存分配策略
默认构造初始化为空字符串_str指向\0。 注意不能初始化为nullptr否则调用c_str时就会对空指针解引用动态扩容当_size达到_capacity时按2倍或需求大小扩容避免频繁内存分配。
构造函数实现
// 默认构造支持传入C字符串
string::string(const char* str) : _size(strlen(str)) {_str new char[_size 1]; // 多分配1字节存放\0strcpy(_str, str);_capacity _size; // 初始容量等于长度
}二、默认成员函数的实现与优化
2.1 拷贝构造函数
传统写法需要手动分配内存并拷贝数据而现代C写法通过“构造临时对象 交换资源”简化代码 (关于swap函数的实现可跳转6.5查找)
// 传统写法易错且冗余
string::string(const string s) {_str new char[s._capacity 1];strcpy(_str, s._str);_size s._size;_capacity s._capacity;
}// 现代写法利用临时对象
string::string(const string s) {string tmp(s._str); // 调用构造函数swap(tmp); // 交换资源
}2.2 赋值运算符重载
通过**“拷贝构造临时对象 交换”**避免自赋值问题同时减少重复代码 //传统写法string string::operator(const string s){if (this ! s){delete[] _str;_str new char[s._capacity 1];strcpy(_str, s._str);_size s._size;_capacity s._capacity;}return *this;}
// 优化版赋值重载
string string::operator(const string s) {if (this ! s) { // 防止自赋值string tmp(s); // 调用拷贝构造swap(tmp); // 交换资源}return *this;
}2.3 析构函数
释放动态内存并将成员变量归零
string::~string() {delete[] _str; // 释放堆内存_size 0;_capacity 0;
}三、迭代器与元素访问
3.1 迭代器实现
模拟原生指针的行为提供begin()和end()
using iterator char*;
iterator begin() { return _str; }
iterator end() { return _str _size; }3.2 运算符重载
通过operator[]提供随机访问并使用assert检查越界
char operator[](size_t i) {assert(i _size); // 越界检查return _str[i];
}四、容量管理
4.1 reserve预分配内存
若需求容量大于当前容量重新分配内存并拷贝数据
void string::reserve(size_t n) {if (n _capacity) {char* tmp new char[n 1]; strcpy(tmp, _str);delete[] _str; // 释放旧内存_str tmp;_capacity n; // 更新容量}
}4.2 resize调整字符串长度
根据新长度截断或填充字符
void string::resize(size_t n, char c) {if (n _size) {_str[n] \0; // 截断_size n;} else {reserve(n); // 确保容量足够for (size_t i _size; i n; i) {_str[i] c; // 填充字符}_size n;_str[_size] \0;}
}五、修改操作
5.1 清空字符串clear
清空字符串内容但不释放内存保留容量
void string::clear() {_str[0] \0; // 首字符置为结束符_size 0; // 长度归零
}5.2 push_back与append
尾插字符检查扩容后直接写入
void string::push_back(char c) {if (_size _capacity) {reserve(_capacity 0 ? 4 : 2 * _capacity);}_str[_size] c;_str[_size] \0;
}追加字符串计算长度后扩容并拷贝
void string::append(const char* str) {size_t len strlen(str);if (_size len _capacity) {reserve(_size len); // 按需扩容}strcpy(_str _size, str); // 直接拷贝_size len;
}5.3 insert与erase
插入字符移动后续字符腾出位置
string string::insert(size_t pos, char c) {assert(pos _size);if (_size _capacity) reserve(2 * _capacity);size_t end _size 1;while (end pos) { // 从后向前移动_str[end] _str[end - 1];end--;}_str[pos] c;_size;return *this;
}删除字符覆盖后续字符并更新长度
string string::erase(size_t pos, size_t len) {assert(pos _size);if (len npos || len _size - pos) {_str[pos] \0;_size pos;} else {strcpy(_str pos, _str pos len); // 覆盖删除区域_size - len;}return *this;
}六、其他关键函数实现
6.1 查找函数find
查找字符
size_t string::find(char c, size_t pos) const {assert(pos _size);for (size_t i pos; i _size; i) {if (_str[i] c) return i;}return npos; // 未找到返回特殊标记
}查找子串
利用标准库的strstr函数优化子串查找
size_t string::find(const char* s, size_t pos) const {assert(pos _size);const char* ptr strstr(_str pos, s); // 直接调用C库函数return ptr ? ptr - _str : npos;
}6.2 子串生成substr
截取从pos开始的len个字符生成新字符串
string string::substr(size_t pos, size_t len) const {assert(pos _size);len (len npos) ? _size - pos : len; // 默认取到末尾len std::min(len, _size - pos); // 防止越界string result;result.reserve(len); // 预分配内存for (size_t i 0; i len; i) {result _str[pos i]; // 逐字符追加}return result;
}6.3 流运算符重载
流插入operator
直接遍历输出有效字符
ostream operator(ostream os, const xc::string s) {for (size_t i 0; i s.size(); i) {os s[i]; // 支持链式调用}return os;
}流提取operator
优化版输入通过缓冲区减少扩容次数
istream operator(istream is, xc::string s) {s.clear(); // 清空原内容char buff[256]; // 局部缓冲区char ch;int idx 0;while (is.get(ch) !isspace(ch)) {buff[idx] ch;if (idx 255) { // 缓冲区满时批量追加buff[idx] \0;s buff;idx 0;}}if (idx 0) { // 处理剩余字符buff[idx] \0;s buff;}return is;
}6.4 比较运算符重载
等于与不等于
bool string::operator(const string s) const {return strcmp(_str, s._str) 0; // 直接比较C字符串
}bool string::operator!(const string s) const {return !(*this s); // 复用等于运算符
}大小比较
bool string::operator(const string s) const {return strcmp(_str, s._str) 0; // 字典序比较
}bool string::operator(const string s) const {return (*this s) || (*this s); // 组合逻辑
}bool string::operator(const string s) const {return !(*this s);
}bool string::operator(const string s) const {return !(*this s);
}6.5 交换函数swap
高效交换两个字符串的资源避免深拷贝
void string::swap(string s) {std::swap(_str, s._str); // 交换指针std::swap(_size, s._size); // 交换长度std::swap(_capacity, s._capacity); // 交换容量
}七、性能优化与注意事项 substr的优化 避免直接使用new和strcpy通过reserve预分配内存减少扩容次数。若需要高性能可实现“浅拷贝引用计数”需处理写时复制逻辑。 find的局限性 当前实现为暴力匹配标准库可能使用更高效的算法如KMP。 流提取的安全性 缓冲区大小固定为256若输入过长可能丢失数据可动态调整缓冲区大小。 swap的优势 仅交换指针和元数据时间复杂度为O(1)适合频繁交换场景。
结语
通过手写string类我们深入理解了basic_string的底层机制。标准库的实现在此基础上进行了大量优化如SSO、内存池但核心逻辑与本文的模拟实现高度一致。掌握这些原理后读者可以更高效地使用std::string并能在需要时定制自己的字符串类。
相关阅读
【C指南】string一string从入门到掌握【C指南】string二深入探究 C basic_string成员变量、函数全解析
关注博主第一时间获取更新