当前位置: 首页 > news >正文

C++ string自定义类的实现

目录

1. 头文件----string.h

1.1 头文件引入与命名空间 

1.2 bit::string 类核心成员(public部分)

1. 迭代器类型声明

2. 迭代器获取函数

3. 构造、析构与拷贝控制

4. 基础属性与访问接口

5. 容量管理

6. 字符串修改操作

7. 字符串查找操作

8. 子串提取与清空

9. 字符串比较运算符重载

1.3 bit::string类私有成员(private部分)

1.4 bit::string 类静态常量(public部分)

1.5 全局流运算符与交换函数(命名空间bit内)

1.6 头文件整体设计总结

2. 实现文件---string.cpp

2.1 静态常量初始化与拷贝控制(核心基础)

1. 静态常量 npos 初始化

2. 构造函数(默认+带参)

3. 析构函数

4. 拷贝构造函数

5. 赋值运算符重载(operator=)

2.2 迭代器与基础访问接口

1. 迭代器接口(begin()/end())

2. 基础访问(c_str()/size()/operator[])

2.3 内存管理(reserve)

2.4 增删查改接口(核心功能)

2.5 比较运算符重载

2.6 全局流运算符与 swap 

2.3 总结


在基本学习完了string类的内容之后 , 小编将用关于string类实现的一篇文章来进一步深入理解string类。小编仍然用string.h头文件 , string.cpp实现文件进行实现:

1. 头文件----string.h

#include<iostream>
#include<string.h>
#include<assert.h>using namespace std;namespace bit
{class string{public:typedef char* iterator;typedef const char* const_iterator;iterator begin();iterator end();const_iterator begin() const;const_iterator end() const;//string();string(const char* str = "");const char* c_str() const;~string();string(const string& s);//string& operator=(const string& s);string& operator=(string s);void swap(string& s);size_t size() const;char& operator[](size_t i);const char& operator[](size_t i) const;void reserve(size_t n);void push_back(char ch);void append(const char* str);string& operator+=(char ch);string& operator+=(const char* str);void pop_back();string& insert(size_t pos, char ch);string& insert(size_t pos, const char* str);string& erase(size_t pos = 0, size_t len = npos);size_t find(char ch, size_t pos = 0) const;size_t find(const char* str, size_t pos = 0)  const;string substr(size_t pos, size_t len = npos) const;void clear();bool operator<(const string& s) const;bool operator<=(const string& s) const;bool operator>(const string& s) const;bool operator>=(const string& s) const;bool operator==(const string& s) const;bool operator!=(const string& s) const;private:char* _str = nullptr;size_t _size = 0;size_t _capacity = 0;// _size < 16 串存在buff数组中// _size >= 16 串存在_str指向的数组中char _buff[16];public:static const size_t npos;};ostream& operator<<(ostream& out, const string& s);istream& operator>>(istream& in, string& s);istream& getline(istream& is, string& str, char delim = '\n');void swap(string& x, string& y);
}

上面是完整代码 , 我们进行详细分析:

该头文件实现了一个自定义string类 , 整体结构包含头文件引入 , 命名空间 , 类成员(变量/函数)声明, 友元函数声明:

1.1 头文件引入与命名空间 

#include<iostream> 

  • 作用:引入标准输入输出流库。
  • 关联:为后续声明 ostream& operator<<(输出) , istream& operator>> (输入)等流运算符重载提供基础 , 因为 ostream , istream 类定义在 <iostream> 中。
#include<string.h> 

  • 作用:引入C语言字符串处理库(如 strlen , strcpy , strcmp , strstr 等函数)。
  • 关联:自定义 string 类的底层字符串操作(如构造时拷贝字符串 , 比较字符串 , 查找子串) , 需依赖这些C函数 , 例如通过 strlen 计算传入字符串长度 , 通过 strcpy 拷贝字符串内容。
#include<assert.h> 
  • 作用:引入断言库 , 提供 assert() 宏。
  • 关联:用于成员函数的参数合法性检查 , 例如 operator[] 访问下标时检查 i <_size , insert / erase 时检查 pos <= _size , 若断言失败则直接终止程序 , 便于调试。
using namespace std; 
  • 作用:引入标准命名空间 std。
  • 关联:使得代码中可直接使用 std 下的类(如 ostream , istream)和函数 , 无需写std::ostream , 简化流运算符重载的声明。
namespace bit { ... } 
  • 作用:定义自定义命名空间 bit。
  • 关联:避免与标准库 std::string 或其他库的 string 类命名冲突 , 使用时需通过 bit::string 指定作用域。

1.2 bit::string 类核心成员(public部分)

1. 迭代器类型声明

typedef char* iterator; 
  • 作用:将 char* (字符指针)这个原生类型 , 重命名为 iterator (普通迭代器类型名)。
  • 功能:用于遍历 , 修改 string 对象的字符(如 begin() 返回首字符地址 , end() 返回尾后字符地址) , 支持 ++ / -- /解引用( * )等指针操作。后续通过 iterator 定义的变量 , 本质就是 char* , 用于遍历和修改 string 对象中的非 const 字符(比如通过 begin() / end() 获取迭代器 , 修改字符串内容)。
  • 与标准库关联:对应 std::string::iterator , 行为一致(如遍历字符串时从 begin() 到 end() )。
typedef const char* const_iterator; 
  • 作用:将 const char* (指向 const 字符的指针)重命名为 const_iterator (const 迭代器类型名)。
  • 功能:用于遍历 const string 对象(或普通 string 的 const 场景) , 只能读取字符 , 不能修改(比如 const 对象调用 begin() const  获取的就是这种迭代器)。
  • 与标准库关联:对应 std::string::const_iterator , 仅支持读操作 , 不支持写操作。

和标准库 std::string 的关系:

  • 标准库 std::string 的迭代器底层也是类似逻辑(普通迭代器通常是 char* 封装 , const 迭代器是 const char* 封装) , 这里的命名(iterator / const_iterator)完全对齐标准库 , 目的是让自定义 string 的使用体验和标准库保持一致(比如都能通过 for (auto it = s.begin(); it != s.end(); ++it) 遍历)。

2. 迭代器获取函数

iterator begin(); 
  • 返回值:iterator(char*) , 指向字符串首字符(_size<16 时指向 _buff[0] , _size>=16 时指向 _str[0] )。
  • 功能 : 获取非const对象的起始迭代器 , 用于修改字符(如 for (auto it = s.begin(); it != s.end(); ++it) *it = 'a'; )。
  • 标准库对应:std::string::begin() , 行为完全一致。
iterator end(); 
  • 返回值: iterator(char*) , 指向字符串尾后字符(_size<16 时指向 _buff[_size] , _size>=16 时指向 _str[_size] )。
  • 功能:标记非const对象的迭代器结束位置(不指向有效字符) , 作为遍历终止条件。
  • 标准库对应:std::string::end() , 行为完全一致。
const_iterator begin() const; 
  • 返回值:const_iterator (const char*) , 指向const对象的首字符
  • 功能:获取const对象的起始迭代器 , 仅用于读字符(如 for (auto it = cs.begin(); it != cs.end(); ++it) cout << *it; , cs 是 const bit::string)。
  • 重载逻辑:const 成员函数 , 仅作用于const对象 , 避免与非const版本冲突。
  • 标准库对应:std::string::begin() const。
const_iterator end() const; 
  • 返回值:const_iterator(const char*) , 指向const对象的尾后字符
  • 功能:标记const对象的迭代器结束位置 , 仅用于读遍历的终止条件。
  • 标准库对应:std::string::end() const。

3. 构造、析构与拷贝控制

 //string(); (注释掉的默认构造)
  • 作用:原默认构造函数被注释 , 因为下面的带参构造用了默认参数 , 可替代默认构造的功能(避免默认构造与带参构造重载冲突)。
string(const char* str = ""); (带默认参数的构造函数)
  • 参数:const char* str , 传入的C风格字符串 , 默认值为空串 "" (覆盖默认构造的需求)。
  • 功能:初始化 string 对象:
  • 若 str 长度( strlen(str) )<16:将 str 拷贝到 _buff , _str 设为 nullptr , _size 设为 strlen(str) , _capacity 设为15( _buff  容量为16 , 留1个位置存 '\0');
  • 若长度>=16 : 从堆申请内存(长度+1 , 存 '\0') , _str  指向堆内存 , 拷贝 str 到 _str , _size 设为 strlen(str) , _capacity 设为长度。
  • 标准库对应:std::string(const char* s = "") , 行为一致(标准库也可能用SSO优化)。
const char* c_str() const; 
  • 返回值:const char* , 指向C风格字符串(以 '\0' 结尾)。
  • 功能:提供与C语言兼容的接口 , 例如将 bit::string 传入需要 const char* 参数的函数(如 printf("%s", s.c_str()); )。
  • 实现逻辑:_size<16 时返回 _buff , _size>=16 时返回 _str (两者均保证以 '\0' 结尾)。
  • 标准库对应:std::string::c_str() , 功能完全一致。
~string(); (析构函数)
  • 功能:释放对象占用的堆内存(避免内存泄漏)。
  • 实现逻辑:仅当 _size >=16 时(_str 指向堆内存) , 才调用 delete[] _str;_size<16 时 _buff 是栈上数组 , 无需手动释放。
  • 标准库对应:std::string::~string() , 同样负责释放堆内存(若有)。
string(const string& s); (拷贝构造函数)
  • 参数:const string& s , 被拷贝的源对象(const保证不修改源对象)。
  • 功能:深拷贝构造新对象(避免浅拷贝导致的内存重复释放/修改冲突)。
  • 实现逻辑:
  • 若 s._size <16 :直接拷贝 s._buff 到新对象的 _buff , _str 设为 nullptr;
  • 若  s._size >=16 :新对象从堆申请与 s 相同容量的内存 , 拷贝 s._str 内容到新内存 , _str 指向新内存;
  • 同时拷贝 s._size 和 s._capacity 到新对象。
  • 标准库对应:std::string::string(const string& s) , 同样是深拷贝(C++11后可能有优化 , 但语义是深拷贝)。
//string& operator=(const string& s); (注释掉的赋值运算符重载)
  • 作用:原传统赋值重载被注释 , 替换为下面的“值传递参数”版本 , 利用拷贝构造简化实现。
string& operator=(string s); (赋值运算符重载,值传递参数)
  • 参数:string s , 源对象的拷贝(值传递 , 会调用拷贝构造生成临时对象 s)。
  • 返回值:string& , 返回当前对象的引用(支持链式赋值 , 如 s1 = s2 = s3;)。
  • 功能:深拷贝赋值(将 s 的内容赋值给当前对象)。
  • 实现逻辑:通过 swap(s) 交换当前对象与临时对象 s 的成员(_str 、_size 、_capacity 、_buff ) , 临时对象 s 析构时会自动释放当前对象原来的堆内存(若有) , 简化代码且避免内存泄漏。
  • 标准库对应:std::string& operator=(const string& s) , 语义一致(深拷贝) , 此版本是“拷贝并交换”(Copy-and-Swap) idiom 的实现 , 更简洁安全。
void swap(string& s); (成员 swap 函数)
  • 参数:string& s , 要交换的另一个对象。
  • 功能:交换两个 string 对象的所有成员( _str 、_size 、_capacity 、_buff )。
  • 实现逻辑:直接交换成员变量(如 std::swap(this->_str, s._str) , std::swap(this->_size , s._size) 等) , 无堆内存申请/释放 , 效率高。
  • 与非成员 swap 的关系:下面的非成员 swap 函数会调用此成员 swap (如 void swap(string& x, string& y) { x.swap(y); } ) , 符合标准库习惯(std::swap 也会优先调用成员 swap)。

4. 基础属性与访问接口

size_t size() const; 
  • 返回值:size_t(无符号整数) , 字符串的有效字符个数(不包含 '\0')。
  • 功能:获取字符串长度(如 s.size() 得到 s 中字符的个数)。
  • 标准库对应:std::string::size() , 功能完全一致(与 std::string::length() 等价 , 此自定义类未实现 length() , 但 size() 是标准命名)。
char& operator[](size_t i); (非const下标访问)
  • 参数:size_t i , 要访问的字符下标(0-based)。
  • 返回值:char& , 对应下标的字符引用(支持修改)。
  • 功能:通过下标访问并修改字符(如 s[0] = 'A';)。
  • 安全检查:内部会用 assert(i < _size) 检查下标合法性(越界则终止程序)。
  • 标准库对应:std::string::operator[](size_t pos) , 行为一致(标准库 operator[] 默认不检查越界 , debug模式可能检查 , 此自定义版本用 assert 强制检查)。
const char& operator[](size_t i) const; (const下标访问)
  • 参数:size_t i , 要访问的字符下标(0-based)。
  • 返回值:const char& , 对应下标的字符const引用(只读 , 不可修改)。
  • 功能:访问const对象的字符(如 const bit::string s = "abc"; cout << s[1]; )。
  • 重载逻辑:const 成员函数 , 仅作用于const对象 , 避免与非const版本冲突 , 同样有 assert(i < _size) 检查。
  • 标准库对应:std::string::operator[](size_t pos) const , 功能一致。

5. 容量管理

void reserve(size_t n); 
  • 参数:size_t n , 期望的最小容量(即能存储 n 个有效字符 , 不包含 '\0')。
  • 功能:预分配容量(仅当 n > _capacity 时生效 , 避免频繁扩容)。
  • 实现逻辑
  • 若 n <= _capacity :不做操作;
  • 若 n <16 :无需申请堆内存(用 _buff  即可) , 仅更新 _capacity 为15;
  • 若 n >=16 :从堆申请 n+1 字节内存(+1存 '\0') , 拷贝原字符串内容到新内存 , 释放原 _str (若有) , 更新 _str 指向新内存 , _capacity 设为 n。
  • 标准库对应:std::string::reserve(size_t n) , 功能一致(标准库 reserve 不缩小容量 , 此版本也遵循)。

6. 字符串修改操作

void push_back(char ch); 
  • 参数:char ch , 要追加的单个字符。
  • 功能:在字符串末尾追加一个字符(如 s.push_back('!'); 将 '!' 加到 s 末尾)。
  • 实现逻辑
  • 1. 检查容量:若  _size +1 > _capacity , 调用 reserve(_size +1) 扩容;
  • 2. 追加字符: _size<16  时写  _buff[_size] = ch , 否则写  _str[_size] = ch;
  • 3. 更新 _size ( _size++ ) , 并在尾后位置写 '\0'(保证  c_str()  有效)。
  • 标准库对应:std::string::push_back(char c) , 功能一致。
void append(const char* str); 
  • 参数:const char* str ,要追加的C风格字符串(需以 '\0' 结尾)。
  • 功能:在字符串末尾追加一个C风格字符串(如  s.append("123");  将 "123" 加到  s  末尾)。
  • 实现逻辑
  • 1. 计算 str 长度 len = strlen(str) ;
  • 2. 检查容量:若 _size + len > _capacity , 调用 reserve(_size + len) 扩容;
  • 3. 拷贝 str 到字符串末尾( _size<16 时拷贝到 _buff[_size] , 否则拷贝到 _str[_size] );
  • 4. 更新 _size (_size += len ) , 尾后写 '\0'。
  • 标准库对应:std::string::append(const char* s) , 功能一致。
string& operator+=(char ch); 
  • 参数: char ch ,要追加的单个字符。
  • 返回值: string& ,当前对象引用(支持链式操作,如  s += 'a' += 'b'; )。
  • 功能:追加单个字符(语义同  push_back ,但支持运算符语法)。
  • 实现逻辑:直接调用  push_back(ch) ,然后返回  *this 。
  • 标准库对应: std::string& operator+=(char c) ,功能一致。
string& operator+=(const char* str); 
  • 参数: const char* str ,要追加的C风格字符串。
  • 返回值: string& ,当前对象引用(支持链式操作,如  s += "ab" += "cd"; )。
  • 功能:追加C风格字符串(语义同  append ,支持运算符语法)。
  • 实现逻辑:直接调用  append(str) ,然后返回  *this 。
  • 标准库对应: std::string& operator+=(const char* s) ,功能一致。
void pop_back(); 
  • 功能:删除字符串末尾的最后一个有效字符(如 s = "abc"; s.pop_back(); 后 s 为 "ab")。
  • 实现逻辑
  • 1. 先通过 assert(_size > 0) 检查字符串非空(空串删除会触发断言);
  • 2. 直接将 _size-- (减少有效字符计数 , 无需真删除字符 , 后续操作会覆盖);
  • 3. 在新的尾后位置(_size 下标处)写 '\0'(保证 c_str() 仍返回合法的C风格字符串)。
  • 标准库对应:std::string::pop_back() , 功能一致(标准库也要求调用前字符串非空 , 否则行为未定义 , 此版本用 assert 强制检查)。
string& insert(size_t pos, char ch); 
  • 参数:
  • size_t pos :插入位置(0-based,pos=0 表示插在开头 , pos=_size 表示插在末尾);
  • char ch :要插入的单个字符。
  • 返回值:string& , 当前对象引用(支持链式操作 , 如 s.insert(1, 'x').insert(3, 'y'); )。
  • 功能:在指定位置插入单个字符(如 s = "abc"; s.insert(1, 'x');  后 s 为 "axbc")。
  • 实现逻辑
  • 1. 断言检查:assert(pos <= _size) (插入位置不能超过字符串长度);
  • 2. 扩容:若 _size +1 > _capacity , 调用  reserve(_size +1);
  • 3. 移动字符:从末尾( _size 下标)到 pos 下标 , 将字符依次后移1位(避免覆盖要插入的位置);
  • 4. 插入字符:_size<16 时写 _buff[pos] = ch , 否则写 _str[pos] = ch;
  • 5. 更新 _size ( _size++ ) , 尾后写 '\0'。
  • 标准库对应:std::string::insert(size_t pos, char c) , 功能一致。
string& insert(size_t pos, const char* str); 
  • 参数
  • size_t pos :插入位置(0-based , 范围 [0, _size] );
  • const char* str :要插入的C风格字符串(需以 '\0' 结尾)。
  • 返回值:string& , 当前对象引用(支持链式操作)。
  • 功能:在指定位置插入C风格字符串(如 s = "abc"; s.insert(1, "123");  后  s  为 "a123bc")。
  • 实现逻辑:
  • 1. 断言检查:assert(pos <= _size) ;
  • 2. 计算 str 长度 len = strlen(str) (若 str 为空串则不操作);
  • 3. 扩容:若 _size + len > _capacity , 调用 reserve(_size + len) ;
  • 4. 移动字符:从末尾到 pos 下标 , 将字符依次后移 len 位;
  • 5. 拷贝字符串:将 str 内容拷贝到 pos 起始的位置( _buff 或 _str);
  • 6. 更新 _size ( _size += len ) , 尾后写 '\0'。
  • 标准库对应:std::string::insert(size_t pos, const char* s) , 功能一致。
string& erase(size_t pos = 0, size_t len = npos); 
  • 参数
  • size_t pos :删除起始位置(0-based) , 默认值 0 (从开头删);
  • size_t len :要删除的字符个数 , 默认值 npos (删除从 pos 到末尾的所有字符)。
  • 返回值:string& , 当前对象引用(支持链式操作)。
  • 功能:删除指定范围的字符(如 s = "abcdef"; s.erase(2, 3);  后 s 为 "abf";s.erase(2); 后 s 为 "ab")。
  • 实现逻辑
  • 1. 断言检查:assert(pos <= _size) (删除起始位置不能越界);
  • 2. 调整删除长度:若 pos + len > _size 或 len == npos , 则 len = _size - pos (避免删超范围);
  • 3. 移动字符:从 pos + len 下标开始 , 将字符依次前移 len 位(覆盖被删除的字符);
  • 4. 更新 _size ( _size -= len ) , 尾后写 '\0';
  • 5. (可选优化)若删除后 _size 大幅减小 , 可考虑缩容(此版本未实现 , 标准库也默认不缩容)。
  • 与 npos 的关联:npos 是静态常量(值通常为 -1 , 因 size_t 是无符号类型 , 实际存储为最大值) , 用于表示“到末尾”的范围 , 与标准库 std::string::npos 语义完全一致。
  • 标准库对应:std::string::erase(size_t pos = 0, size_t len = npos) , 功能一致。

7. 字符串查找操作

size_t find(char ch, size_t pos = 0) const; 
  • 参数:
  • char ch :要查找的目标字符;
  • size_t pos :查找起始位置(0-based) , 默认值 0 (从开头找)。
  • 返回值:size_t , 找到则返回目标字符的下标 , 未找到则返回 npos。
  • 功能:从指定位置开始查找单个字符(如 s = "abcabc"; s.find('b', 2); 返回 4)。
  • 实现逻辑:
  • 1. 遍历范围:从 pos 到 _size -1;
  • 2. 匹配字符:若 _size<16 则遍历 _buff[i] , 否则遍历 _str[i] , 找到与 ch 相等的字符则返回 i;
  • 3. 未找到:返回 npos。
  • 标准库对应:std::string::find(char c, size_t pos = 0) const , 返回值语义完全一致。
size_t find(const char* str, size_t pos = 0) const; 
  • 参数
  • const char* str :要查找的目标C风格字符串(需以 '\0' 结尾);
  • size_t pos :查找起始位置 , 默认值 0。
  • 返回值:size_t , 找到则返回目标字符串的起始下标 , 未找到则返回 npos。
  • 功能:从指定位置开始查找子串(如 s = "abcdef"; s.find("cd", 1);  返回 2)。
  • 实现逻辑
  • 1. 边界检查:若 pos >= _size 或 str 为空串 , 返回 npos;
  • 2. 调用C函数:获取当前字符串的C风格指针( c_str() ) , 调用 strstr(_cstr + pos, str) (strstr 是  <string.h> 中的子串查找函数);
  • 3. 计算下标:若 strstr 返回非空指针 , 下标 = 返回指针 -  c_str() ;若返回空指针 , 返回 npos。
  • 标准库对应:std::string::find(const char* s, size_t pos = 0) const , 功能一致(标准库内部可能不用 strstr , 但语义相同)。

8. 子串提取与清空

string substr(size_t pos, size_t len = npos) const; 
  • 参数
  • size_t pos :子串起始位置(0-based);
  • size_t len :子串长度 , 默认值 npos(子串从 pos 到末尾)。
  • 返回值:string , 提取出的子串(新对象 , 深拷贝)。
  • 功能:提取指定范围的子串(如 s = "abcdef"; s.substr(2, 3); 返回 "cde";s.substr(4); 返回 "ef")。
  • 实现逻辑
  • 1. 断言检查:assert(pos <= _size);
  • 2. 调整子串长度:若 pos + len > _size 或 len == npos , 则 len = _size - pos;
  • 3. 创建新对象:构造一个新的 string 对象 , 将当前字符串 pos 开始 , 长度为 len 的字符拷贝到新对象中;
  • 4. 返回新对象。
  • 标准库对应:std::string::substr(size_t pos = 0, size_t len = npos) const , 功能完全一致。
 void clear(); 
  • 功能:清空字符串(仅保留空串 , 不释放容量)。
  • 实现逻辑
  • 1. 将 _size 设为 0;
  • 2. 在 _buff[0] (_size<16 )或 _str[0] ( _size>=16) 处写 '\0' (保证 c_str() 返回空串);
  • 3. 不修改 _capacity 和 _str (容量保留 , 后续插入无需重新扩容)。
  • 标准库对应:std::string::clear() , 功能一致(标准库也不释放容量 , 仅清空内容)。

9. 字符串比较运算符重载

bool operator<(const string& s) const; 
  • 参数:const string& s , 用于比较的另一个字符串对象。
  • 返回值:bool , 当前对象 < 比较对象则返回 true , 否则返回 false。
  • 功能:按字典序比较两个字符串(如 "abc" < "abd" 返回 true , "abc" < "ab" 返回 false )。
  • 实现逻辑:调用 strcmp(c_str(), s.c_str()) ( <string.h> 中的字符串比较函数) , 若返回值 < 0 则返回 true , 否则返回 false。
  • 标准库对应:std::string::operator<(const string& s) const , 字典序规则完全一致。
bool operator<=(const string& s) const; 
  • 返回值:bool , 当前对象 <= 比较对象则返回 true。
  • 实现逻辑:复用 < 和 == 运算符 , 即 return *this < s || *this == s; 。
  • 标准库对应 : std::string::operator<=(const string& s) const , 语义一致。
bool operator>(const string& s) const; 
  • 返回值:bool , 当前对象 > 比较对象则返回 true。
  • 实现逻辑:复用 < 运算符 , 即 return !( *this <= s ); (当前对象不小于等于对方 , 即大于对方)。
  • 标准库对应:std::string::operator>(const string& s) const , 语义一致。
bool operator>=(const string& s) const; 
  • 返回值:bool , 当前对象 >= 比较对象则返回 true。
  • 实现逻辑:复用 < 运算符 , 即 return !( *this < s ); 。
  • 标准库对应:std::string::operator>=(const string& s) const , 语义一致。
bool operator==(const string& s) const; 
  • 返回值:bool , 当前对象与比较对象完全相等则返回 true。
  • 实现逻辑
  • 1. 先比较 _size:若 _size != s._size , 直接返回 false;
  • 2. 再比较内容:调用  strcmp(c_str(), s.c_str()) , 若返回值 == 0 则返回 true , 否则返回 false (避免因 _size 相同但内容不同导致误判)。
  • 标准库对应:std::string::operator==(const string& s) const , 语义一致。
bool operator!=(const string& s) const; 
  • 返回值:bool , 当前对象与比较对象不相等则返回 true。
  • 实现逻辑:复用 == 运算符 , 即 return !( *this == s ); 。
  • 标准库对应:std::string::operator!=(const string& s) const , 语义一致。

1.3 bit::string类私有成员(private部分)

char* _str = nullptr; 
  • 作用:存储长字符串的堆内存指针(小字符串优化的核心成员)。
  • 使用逻辑
  • 当 _size < 16 (短字符串):_str 保持 nullptr , 字符串内容存在 _buff 数组中;
  • 当 _size >= 16 (长字符串):_str 指向堆上申请的内存(存储字符串内容 , 以 '\0' 结尾)。
  • 与标准库的差异:标准库  std::string  也用SSO , 但 _str 可能被优化为“联合体(union)”(节省内存) , 此版本用独立的 _str 和 _buff , 实现更简单但占用内存略多( _str 始终占8字节 , _buff 占16字节)。
size_t _size = 0; 
  • 作用:存储字符串的有效字符个数(不包含末尾的 '\0' )。
  • 初始值:默认初始化为 0 (C++11后类内成员可直接初始化) , 空字符串对象的 _size 为 0。
  • 与其他成员的关联:_size 决定 operator[] , pop_back() , insert() 等函数的操作范围 , 也是 size() 函数的返回值。
size_t _capacity = 0; 
  • 作用:存储字符串的容量(即当前可存储的最大有效字符个数 , 不包含 '\0')。
  • 使用逻辑
  • 当 _size < 16 :_capacity 固定为 15 (_buff 数组大小16 , 留1字节存 '\0' );
  • 当 _size >= 16 :_capacity 为堆内存的有效容量(如申请20字节内存 , _capacity  为19)。
  • 与  reserve()  的关联:reserve(n) 仅当 n > _capacity 时才扩容 , 并更新 _capacity 为 n。
char _buff[16]; 
  • 作用:存储短字符串的栈数组(小字符串优化的核心成员) , 大小为16字节(可存15个有效字符 + 1个 '\0' )。
  • 使用逻辑:_size < 16 时 , 字符串内容直接存在 _buff  中(无需申请堆内存 , 避免内存碎片 , 提高效率);_size >=16 时 , _buff  闲置(此版本未复用 , 属于简单实现)。

1.4 bit::string 类静态常量(public部分)

static const size_t npos; 
  • 作用:表示“无效下标”或“到末尾”的静态常量(全类共享 , 仅需定义一次)。
  • 值的约定:通常在 .cpp 文件中定义为 const size_t bit::string::npos = -1; (因 size_t 是无符号类型 , -1 会被解释为该类型的最大值 , 确保大于任何合法下标)。
  • 与成员函数的关联:erase() , find() , substr() 等函数用 npos 作为默认参数或返回值 , 语义与标准库 std::string::npos 完全一致。

1.5 全局流运算符与交换函数(命名空间bit内)

ostream& operator<<(ostream& out, const string& s); (输出运算符重载)
  • 参数
  • ostream& out :输出流对象(如 cout ) , 传引用是为了链式输出(如 cout << s1 << s2; );
  • const string& s :要输出的 string 对象(const保证不修改 , 传引用避免拷贝)。
  • 返回值:ostream& , 返回 out 的引用 , 支持链式输出。
  • 功能:实现 string 对象的流式输出(如 cout << s; , 直接打印字符串内容 , 而非地址)。
  • 实现逻辑(.cpp 中):遍历 s 的字符(从 s.begin() 到 s.end() ) , 通过 out.put(c) 或 out << c 逐个输出 , 最终返回 out。
  • 与标准库的关联:对应 std::ostream& operator<<(std::ostream&, const std::string&) , 使用方式完全一致 , 是 string 类的核心全局接口之一(需声明为友元或通过 c_str() 访问内容 , 此头文件未显式声明友元 , 推测 .cpp 中通过 c_str() 实现: out << s.c_str(); , 更简洁)。
     
istream& operator>>(istream& in, string& s); (输入运算符重载)
  • 参数:
  • istream& in :输入流对象(如 cin ) , 传引用支持链式输入;
  • string& s :存储输入内容的 string 对象(非const , 需修改)。
  • 返回值:istream& , 返回 in 的引用 , 支持链式输入(如 cin >> s1 >> s2; )。
  • 功能:实现 string 对象的流式输入(如 cin >> s; ) , 默认跳过空白字符(空格、回车、制表符等) , 直到遇到下一个空白字符停止。
  • 实现逻辑(.cpp 中)
  • 1. 先调用 s.clear() 清空 s 原有内容;
  • 2. 定义临时字符 char ch , 通过 in.get(ch) 读取字符 , 跳过开头的空白字符(while (isspace(ch)) in.get(ch); );
  • 3. 循环读取非空白字符 , 调用 s.push_back(ch) 追加到 s , 直到读取到空白字符或流结束;
  • 4. 返回 in。
  • 与标准库的关联:对应 std::istream& operator>>(std::istream&, std::string&) , 行为一致(默认跳空白) , 但不支持读取带空格的字符串(需用 getline )。
istream& getline(istream& is, string& str, char delim = '\n'); (全局getline函数)
  • 参数
  • istream& is :输入流对象(如 cin);
  • string& str :存储输入内容的 string 对象;
  • char delim :终止字符(默认是换行符 '\n' ) , 表示读取到该字符时停止(不包含终止字符)。
  • 返回值:istream& , 返回 is 的引用 , 支持链式调用。
  • 功能:读取一行字符串(包括空格) , 直到遇到终止字符 delim 或流结束(解决 operator>> 不能读空格的问题) , 如 getline(cin, s);  读取一整行输入。
  • 实现逻辑(.cpp 中)
  • 1. 调用 str.clear() 清空原有内容;
  • 2. 定义临时字符 char ch , 通过 is.get(ch) 循环读取字符;
  • 3. 若读取到的字符不是 delim 且流未结束 , 调用 str.push_back(ch) 追加;若读取到 delim , 终止循环(不将 delim 加入 str );
  • 4. 返回 is。
  • 与标准库的关联:对应 std::getline(std::istream&, std::string&, char) , 功能和参数完全一致 , 是读取带空格字符串的核心接口。
void swap(string& x, string& y); (全局swap函数)
  • 参数:string& x , string& y :要交换的两个 string 对象(传引用避免拷贝)。
  • 功能:交换两个 string 对象的内容(替代  std::swap ,效率更高)。
  • 实现逻辑(.cpp 中):直接调用成员函数 x.swap(y) (成员 swap 直接交换成员变量 , 无堆内存操作 , 比 std::swap 的“拷贝-赋值-拷贝”效率高)。
  • 与标准库的关联:符合C++标准习惯——为自定义类型提供全局 swap 函数 , 并优先调用成员 swap , 确保 std::swap(x, y) 时也能匹配到高效的自定义实现( std::swap 内部会检测是否有自定义 swap , 若有则调用)。

1.6 头文件整体设计总结

  • 1. 核心优化:采用 小字符串优化(SSO) , 通过 _buff[16] 存储短字符串(<16字符) , 避免频繁堆内存申请 , 提升效率;长字符串用 _str 指向堆内存 , 保证扩展性。
  • 2. 接口完整性:覆盖 std::string 的核心接口(构造/析构/拷贝赋值 , 迭代器 , 容量管理 , 修改/查找/比较操作 , 流输入输出) , 使用方式与标准库高度兼容 , 降低使用成本。
  • 3. 安全与效率平衡:用 assert 做参数合法性检查(调试阶段防越界) , 用“拷贝并交换”实现赋值重载(简化代码且避免内存泄漏) , 全局 swap 复用成员函数(保证效率)。
  • 4. 依赖与关联:依赖 <iostream> (流操作) , <string.h> (C字符串函数) , <assert.h> (调试检查) , 成员函数间高度复用(如 operator+= 调用 push_back / append , 比较运算符复用 strcmp 和 == ) , 逻辑清晰且减少冗余。 

2. 实现文件---string.cpp

#include"string.h"namespace bit
{const size_t string::npos = -1;/*string::string():_str(new char[1]{'\0'}),_size(0),_capacity(0){}*/string::string(const char* str):_size(strlen(str)){//cout << "string::string(const char* str)" << endl;_capacity = _size;_str = new char[_size + 1];//strcpy(_str, str);memcpy(_str, str, _size + 1);}string::~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}// s3(s1)/*string::string(const string& s){cout << "string::string(const string& s)" << endl;_str = new char[s._capacity + 1];memcpy(_str, s._str, s._size + 1);_size = s._size;_capacity = s._capacity;}*/void string::swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}// s3(s1)string::string(const string& s){cout << "string::string(const string& s)" << endl;string tmp(s._str);swap(tmp);}// s1 = s2/*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;}*/// s1 = s2/*string& string::operator=(const string& s){if (this != &s){string tmp(s);swap(tmp);}return *this;}*/string& string::operator=(string tmp){cout << "string& string::operator=(string tmp)" << endl;swap(tmp);return *this;}string::iterator string::begin(){return _str;}string::iterator string::end(){return _str + _size;}string::const_iterator string::begin() const{return _str;}string::const_iterator string::end() const{return _str + _size;}const char* string::c_str() const{return _str;}size_t string::size() const{return _size;}char& string::operator[](size_t i){assert(i < _size);return _str[i];}const char& string::operator[](size_t i) const{assert(i < _size);return _str[i];}void string::reserve(size_t n){if (n > _capacity){cout << "reserve:" << n << endl;char* tmp = new char[n + 1];//strcpy(tmp, _str);memcpy(tmp, _str, _size + 1);delete[] _str;_str = tmp;_capacity = n;}}void string::push_back(char ch){if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(newcapacity);}_str[_size] = ch;++_size;_str[_size] = '\0';}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);}//strcpy(_str+_size, str);memcpy(_str + _size, str, len + 1);_size += len;}string& string::operator+=(char ch){push_back(ch);return *this;}string& string::operator+=(const char* str){append(str);return *this;}void string::pop_back(){assert(_size > 0);--_size;_str[_size] = '\0';}string& string::insert(size_t pos, char ch){assert(pos <= _size);if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(newcapacity);}// 挪动数据/*int end = _size;while (end >= (int)pos){_str[end + 1] = _str[end];--end;}*/size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];--end;}_str[pos] = ch;++_size;return *this;}string& string::insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);if (_size + len > _capacity){size_t newcapacity = 2 * _capacity > _size + len ? 2 * _capacity : _size + len;reserve(newcapacity);}// 挪动数据/*int end = _size;while (end >= (int)pos){_str[end + len] = _str[end];--end;}*/size_t end = _size + len;while (end > pos + len - 1){_str[end] = _str[end - len];--end;}for (size_t i = 0; i < len; i++){_str[pos + i] = str[i];}_size += len;return *this;}string& string::erase(size_t pos, size_t len){assert(pos < _size);// 要删除的数据,大于pos后面的字符个数// pos后面全删if (len == npos || len >= (_size - pos)){_size = pos;_str[_size] = '\0';}else{size_t i = pos + len;memmove(_str + pos, _str + i, _size + 1 - i);_size -= len;}return *this;}size_t string::find(char ch, size_t pos)  const{for (size_t i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return npos;}size_t string::find(const char* str, size_t pos)  const{// kmpconst char* p1 = strstr(_str + pos, str);if (p1 == nullptr){return npos;}else{return p1 - _str;}}string string::substr(size_t pos, size_t len) const{if (len == npos || len >= _size - pos){len = _size - pos;}string ret;ret.reserve(len);for (size_t i = 0; i < len; i++){ret += _str[pos + i];}//cout << &ret << endl;return ret;}// s1 < s2// "hello"  "hello"   -> false// "hellox" "hello"   -> false// "hello"  "hellox"  -> truebool string::operator<(const string& s) const{size_t i1 = 0, i2 = 0;while (i1 < _size && i2 < s._size){if (_str[i1] < s[i2]){return true;}else if (_str[i1] > s[i2]){return false;}else{++i1;++i2;}}return i2 < s._size;}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);}bool string::operator==(const string& s) const{size_t i1 = 0, i2 = 0;while (i1 < _size && i2 < s._size){if (_str[i1] != s[i2]){return false;}else{++i1;++i2;}}return i1 == _size && i2 == s._size;}bool string::operator!=(const string& s) const{return !(*this == s);}void string::clear(){_str[0] = '\0';_size = 0;}ostream& operator<<(ostream& out, const string& s){//out << s.c_str();for (size_t i = 0; i < s.size(); i++){out << s[i];}return out;}istream& operator>>(istream& in, string& s){s.clear();char buff[128];int i = 0;char ch = in.get();while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == 127){buff[i] = '\0';s += buff;i = 0;}ch = in.get();}if (i > 0){buff[i] = '\0';s += buff;}return in;}istream& getline(istream& in, string& s, char delim){s.clear();char buff[128];int i = 0;char ch = in.get();while (ch != delim){buff[i++] = ch;if (i == 127){buff[i] = '\0';s += buff;i = 0;}ch = in.get();}if (i > 0){buff[i] = '\0';s += buff;}return in;}void swap(string& x, string& y){x.swap(y);}
}

2.1 静态常量初始化与拷贝控制(核心基础)

这部分是自定义 string 的“基石” , 决定内存管理和对象拷贝的安全性 , 实现整体规范 , 采用了业内推荐的高效写法。

1. 静态常量 npos 初始化

const size_t string::npos = -1; 
  • 分析:
  • 正确性:npos 是  string  类的“无效位置”标记 , 用 -1 初始化(因 size_t 是无符号类型 , -1 会被解释为该类型的最大值) , 符合 C++ 标准库 std::string 的设计 , 后续  find , erase 等接口用它判断“未找到”或“删除至末尾” , 逻辑正确。
  • 注意:需确保头文件中已声明 static const size_t npos; , 当前实现与头文件匹配。

2. 构造函数(默认+带参)

带参构造:

	string::string(const char* str):_size(strlen(str)){//cout << "string::string(const char* str)" << endl;_capacity = _size;_str = new char[_size + 1];//strcpy(_str, str);memcpy(_str, str, _size + 1);}
  • 逻辑:计算字符串长度 _size = strlen(str) , 按长度开辟内存(_size + 1 , 预留 \0 空间) , 用 memcpy 拷贝字符串。
  • 优点:用 memcpy 替代 strcpy 更高效( memcpy 直接按字节拷贝 , 无需判断 \0 , 且已知长度 _size + 1) , 边界处理正确(给 \0 留位置)。
/*string::string():_str(new char[1]{'\0'}),_size(0),_capacity(0)
{}*/
  • 默认构造:头文件中默认构造已被注释(合并到带参构造的默认参数 const char* str = "" ) , 实现中也注释了独立默认构造 , 逻辑一致(带参构造传入空串时 , _size=0 , _capacity=0 , _str 指向长度为1的 \0 字符串) , 无问题。

3. 析构函数

	string::~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}
  • 逻辑:释放 _str 指向的动态内存 , 将成员置空/置 0。
  • 正确性:无内存泄漏风险( new[] 分配的内存用 delete[] 释放) 。

4. 拷贝构造函数

第一段注释的拷贝构造函数(传统写法)

/*string::string(const string& s)
{cout << "string::string(const string& s)" << endl;_str = new char[s._capacity + 1];memcpy(_str, s._str, s._size + 1);_size = s._size;_capacity = s._capacity;
}*/
  • 作用与逻辑:
  • 这是最基础的深拷贝构造函数实现 , 用于创建一个新的 string 对象 , 拷贝参数 s 的内容:
  • _str = new char[s._capacity + 1] : 为新对象的 _str 动态分配内存(容量和原对象一致 , +1 是为存储字符串结束符 \0 )。
  • memcpy(_str, s._str, s._size + 1) :把原对象 s 的字符串内容(包括 \0 )拷贝到新分配的内存。
  • _size = s._size; _capacity = s._capacity :同步原对象的字符串长度和容量。 

 第二段拷贝构造函数(优化写法)

	void string::swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}// s3(s1)string::string(const string& s){cout << "string::string(const string& s)" << endl;string tmp(s._str);swap(tmp);}
  • 优化思路:
  • 这是利用 “拷贝 - 交换(Copy-Swap)” 惯用法的写法 , 优点是:
  • 1. 代码简洁:借助已有的构造函数(string tmp(s._str)调用带 const char* 参数的构造函数)和 swap 成员函数 , 避免重复写“分配内存、拷贝数据、同步大小容量”的逻辑。
  • 2. 异常安全:如果 string tmp(s._str) 构造过程中抛异常(比如内存分配失败) , 当前对象的状态不会被破坏(因为 tmp 还没和当前对象交换)。
  • 具体逻辑:
  • string tmp(s._str) :先用原对象的 _str 构造一个临时对象 tmp (完成深拷贝)。
  • swap(tmp) :通过 swap 成员函数 , 把当前对象和 tmp 的资源( _str 、_size 、_capacity)交换——当前对象获得 tmp 的拷贝数据 , tmp 则接管当前对象原来的(可能无效或旧的)资源 , 函数结束时 tmp 析构 , 自动释放这些旧资源。

 总结:

  • 第一段是传统深拷贝写法 , 逻辑直接但代码冗余;
  • 第二段是拷贝 - 交换优化写法 , 利用已有接口简化代码 , 提升异常安全性。
  • 通常实际项目里会优先选第二种优化写法 , 所以把第一段传统实现注释掉了。

5. 赋值运算符重载(operator=)

第一种:传统深拷贝写法(被注释的 operator=)

/*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; 
}*/
  • 逻辑步骤:
  • if (this != &s) :避免自赋值(如 s1 = s1 , 防止释放自身内存后非法访问)。
  • char* tmp = new char[s._capacity + 1] : 为新数据分配内存(容量和原对象s一致 , +1存 \0)。
  • memcpy(tmp, s._str, s._size + 1)) : 拷贝原对象 s 的字符串内容(包括 \0)到临时内存 tmp。
  • delete[] _str :释放当前对象旧内存 , 防止内存泄漏。
  • _str = tmp; _size = s._size; _capacity = s._capacity :更新当前对象的资源(指向新内存 , 同步大小和容量)。
  • return *this:返回当前对象 , 支持链式赋值(如 s1 = s2 = s3 )。
  • 缺点:
  • 若 new char[...] 失败(内存不足抛异常), 当前对象的 _str 已被 delete[] 释放 , 会变成无效状态(异常不安全)。

第二种:拷贝 - 交换优化写法(被注释的operator=)

/*string& string::operator=(const string& s)
{if (this != &s){string tmp(s); swap(tmp); }return *this; 
}*/
  • 逻辑步骤:
  • if (this != &s) :同样避免自赋值。
  • string tmp(s) :用拷贝构造函数创建临时对象 tmp(深拷贝原对象 s 的资源 , 若构造失败 , 当前对象状态不受影响)。
  • swap(tmp) :通过 swap 成员函数 , 交换当前对象和 tmp 的资源(当前对象获得 tmp 的新拷贝数据 , tmp 接管当前对象旧资源)。
  • 函数结束时 , tmp 析构(自动调用 ~string() 释放旧资源 , 无需手动管理)。
  • 优点:
  • 异常安全:若 string tmp(s) 抛异常(如内存分配失败) , 当前对象的旧资源还在 , 不会被破坏。
  • 代码简洁:复用拷贝构造和 swap 逻辑 , 无需重复写“分配、拷贝、释放”代码。

第三种 : 值传递版赋值运算符重载

string& string::operator=(string tmp)
{cout << "string& string::operator=(string tmp)" << endl;swap(tmp);return *this;
}

核心逻辑 : 这是 赋值运算符重载的“值传递的最终优化版” , 利用 C++ 的值传递特性 + swap 惯用法,实现高效、异常安全的赋值:

  • 1. 参数是值传递( string tmp ):
  • 调用该函数时 , 编译器会自动拷贝一份实参(如 s1 = s2 中 , s2 会被拷贝给 tmp) , 相当于“自动帮你完成深拷贝”。
  • 2. swap(tmp):
  • 交换当前对象(*this)和 tmp 的资源( _str , _size , _capacity 等)。交换后:
  • - 当前对象获得 tmp 的新拷贝数据(完成赋值);
  • -  tmp 持有当前对象的旧资源,函数结束后 tmp 会自动析构(调用 ~string() 释放旧资源)。
  • 3. return *this :支持链式赋值(如 s1 = s2 = s3 )。

优点

  • 异常安全:
  • 若拷贝实参给 tmp 时抛异常(如内存不足) , 当前对象的状态不会被修改(因为 tmp 还没和它交换)。
  • 代码极简:
  • 无需手动写“分配内存、拷贝数据、释放旧内存” , 借助值传递和 swap 一键完成。
  • 自赋值安全:
  • 即使 s1 = s1 , 值传递会拷贝一份 s1 给 tmp , 交换后不影响逻辑(等价自交换)。

2.2 迭代器与基础访问接口

这部分接口是 string 支持“范围 for” , 随机访问的核心 , 实现完全正确 , 符合迭代器设计规范。

1. 迭代器接口(begin()/end())

string::iterator string::begin()
{return _str;
}string::iterator string::end()
{return _str + _size;
}string::const_iterator string::begin() const
{return _str;
}string::const_iterator string::end() const
{return _str + _size;
}
  • 实现:begin() 返回 _str(首字符地址) , end() 返回 _str + _size(尾字符的下一个位置 , 符合“左闭右开”区间)。
  • 正确性:
  • 普通迭代器( iterator) : 返回 char* , 支持读写;
  • const 迭代器( const_iterator ) : 返回 const char* , 仅支持读 , 符合 const 正确性。
  • 配合范围 for 使用时 , for (auto ch : s) 会自动调用 begin() 和 end() , 逻辑正常。

2. 基础访问(c_str()/size()/operator[])

const char* string::c_str() const
{return _str;
}size_t string::size() const
{return _size;
}char& string::operator[](size_t i)
{assert(i < _size);return _str[i];
}const char& string::operator[](size_t i) const
{assert(i < _size);return _str[i];
}
  • c_str() :返回 _str , 符合标准(需确保 _str 以 \0 结尾 , 当前构造/修改接口均满足 , 无问题)。
  • size() :直接返回 _size , 简洁正确( _size 记录字符串有效长度 , 不含 \0 )。
  • operator[] :
  • 用 assert(i < _size) 检查下标合法性 , 避免越界访问。
  • 普通版本返回 char&(支持修改 , 如 s[0] = 'a') , const 版本返回 const char&(仅读) , 符合const正确性。

2.3 内存管理(reserve)

void string::reserve(size_t n)
{if (n > _capacity){cout << "reserve:" << n << endl;char* tmp = new char[n + 1];//strcpy(tmp, _str);memcpy(tmp, _str, _size + 1);delete[] _str;_str = tmp;_capacity = n;}
}

reserve 是控制容量 , 减少扩容次数的关键接口 , 实现逻辑正确。

  • 实现逻辑:若 n > _capacity , 则开辟 n + 1 字节的新内存 , 拷贝旧数据到新内存 , 释放旧内存 , 更新 _str 和 _capacity。
  • 优点
  • 仅在 n 大于当前容量时扩容 , 避免无效操作。
  • 预留 n + 1 字节(含 \0) , 后续修改无需额外扩容。

2.4 增删查改接口(核心功能)

1. 尾部增加(push_back/append/operator+=)

void string::push_back(char ch)
{if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(newcapacity);}_str[_size] = ch;++_size;_str[_size] = '\0';
}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);}//strcpy(_str+_size, str);memcpy(_str + _size, str, len + 1);_size += len;
}string& string::operator+=(char ch)
{push_back(ch);return *this;
}string& string::operator+=(const char* str)
{append(str);return *this;
}
  • push_back(char ch) :
  • 扩容判断:若 _size >= _capacity , 按“0 则扩 4 , 否则扩 2 倍”的策略扩容(符合标准库的扩容逻辑 , 平衡性能和内存)。
  • 操作:在 _str[_size] 赋值 ch , _size 自增后补 \0 , 确保字符串结尾正确。
  • append(const char* str) :
  • 计算 str 长度 len , 若 _size + len 超过容量则扩容(扩容到 2*_capacity 和 _size+len 的较大值)。
  • 用 memcpy 直接拷贝 str 到 _str + _size , 效率高于循环赋值 , 最后更新 _size。
  • operator+= :直接调用 push_back 或 append , 代码复用性好 , 逻辑一致

2. 尾部删除(pop_back)

void string::pop_back()
{assert(_size > 0);--_size;_str[_size] = '\0';
}
  • 逻辑 : assert(_size > 0)确保非空 , _size 自减后补 \0 , 简洁正确。
  • 注意:不修改 _capacity (string的容量默认只增不减 , 符合标准行为)。

3. 插入(insert : 单字符/字符串)

string& string::insert(size_t pos, char ch)
{assert(pos <= _size);if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(newcapacity);}// 挪动数据/*int end = _size;while (end >= (int)pos){_str[end + 1] = _str[end];--end;}*/size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];--end;}_str[pos] = ch;++_size;return *this;
}string& string::insert(size_t pos, const char* str)
{assert(pos <= _size);size_t len = strlen(str);if (_size + len > _capacity){size_t newcapacity = 2 * _capacity > _size + len ? 2 * _capacity : _size + len;reserve(newcapacity);}// 挪动数据/*int end = _size;while (end >= (int)pos){_str[end + len] = _str[end];--end;}*/size_t end = _size + len;while (end > pos + len - 1){_str[end] = _str[end - len];--end;}for (size_t i = 0; i < len; i++){_str[pos + i] = str[i];}_size += len;return *this;
}
  • 共性优点:
  • 用 assert(pos <= _size) 检查插入位置合法性(pos 可等于 _size , 即尾部插入)。
  • 扩容逻辑正确(单字符插入判断 _size >= _capacity , 字符串插入判断 _size + len >_capacity)。
  • 数据挪动采用“无符号变量循环” (size_t end = ... ) , 避免注释中“int 强转”的潜在问题(如 pos 超过 int 范围导致循环错误)。
  • 细节优化点:
  • 字符串插入中 , “循环赋值 str 到 _str”可替换为 memcpy(_str + pos, str, len) , 效率更高(已知 len 长度 , 无需循环)。

4. 删除(erase)

string& string::erase(size_t pos, size_t len)
{assert(pos < _size);// 要删除的数据,大于pos后面的字符个数// pos后面全删if (len == npos || len >= (_size - pos)){_size = pos;_str[_size] = '\0';}else{size_t i = pos + len;memmove(_str + pos, _str + i, _size + 1 - i);_size -= len;}return *this;
}
  • 逻辑:
  • 边界判断:assert(pos < _size) 确保删除位置有效。
  • 两种场景:
  • 1. 若 len 是 npos 或 len >= _size - pos (删除长度超过剩余字符) , 直接将 _size 设为 pos 并补 \0 (尾部截断 , 高效)。
  • 2. 否则用 memmove 挪动后续字符(memmove 支持内存重叠 , 比 memcpy 更安全 , 此处 _str + pos 和 _str + i 有重叠 , 必须用 memmove)。
  • 正确性:无内存越界 , 逻辑完全符合预期。

5. 查找(find : 单字符/字符串)

size_t string::find(char ch, size_t pos)  const
{for (size_t i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return npos;
}size_t string::find(const char* str, size_t pos)  const
{// kmpconst char* p1 = strstr(_str + pos, str);if (p1 == nullptr){return npos;}else{return p1 - _str;}
}

  • 单字符查找:循环遍历 _str 从 pos 开始的字符 , 找到返回下标 , 否则返回 npos , 逻辑简单正确。
  • 字符串查找:直接调用 strstr (C 标准库函数 , 查找子串首次出现位置) , 返回值转换为相对于 _str 的下标 , 代码简洁。
  • 注意:strstr 依赖 _str 以 \0 结尾 , 当前 string 的所有修改接口均满足 , 无问题。

6. 子串(substr)

	string string::substr(size_t pos, size_t len) const{if (len == npos || len >= _size - pos){len = _size - pos;}string ret;ret.reserve(len);for (size_t i = 0; i < len; i++){ret += _str[pos + i];}//cout << &ret << endl;return ret;}
  • 逻辑
  • 边界处理:若 len 是 npos 或 len >= _size - pos , 则 len 设为 _size - pos (避免越界)。
  • 构造返回值:创建空 ret , 预留 len 容量(减少扩容) , 循环赋值子串字符 , 最后返回 ret。
  • 正确性:无问题 , 若想优化可将循环赋值替换为 memcpy (需先 reserve , 再拷贝后手动更新 _size 和 \0 )。

7. 清空( clear )

void string::clear()
{_str[0] = '\0';_size = 0;
}
  • 逻辑:将 _str[0] 设为 \0 , _size 置0 , 不修改 _capacity。
  • 正确性:符合标准库行为( clear 只清空内容 , 不释放容量) , 高效(无需释放内存)。

2.5 比较运算符重载

// s1 < s2
// "hello"  "hello"   -> false
// "hellox" "hello"   -> false
// "hello"  "hellox"  -> true
bool string::operator<(const string& s) const
{size_t i1 = 0, i2 = 0;while (i1 < _size && i2 < s._size){if (_str[i1] < s[i2]){return true;}else if (_str[i1] > s[i2]){return false;}else{++i1;++i2;}}return i2 < s._size;
}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);
}bool string::operator==(const string& s) const
{size_t i1 = 0, i2 = 0;while (i1 < _size && i2 < s._size){if (_str[i1] != s[i2]){return false;}else{++i1;++i2;}}return i1 == _size && i2 == s._size;
}bool string::operator!=(const string& s) const
{return !(*this == s);
}

实现了所有比较运算符(< / <= / > / >= / == / != ) , 逻辑严格遵循字符串比较规则(按ASCII码逐字符比较 , 长度短且前缀相同则短串更小)。

  • 核心逻辑:以 < 和 == 为基础 , 其他运算符通过“逻辑取反”或“组合”实现(如 >= 是 !(*this < s) ) , 代码复用性极高 , 避免重复逻辑。
  • 正确性
  • ==  需判断“所有字符相同且长度相同”(i1 == _size && i2 == s._size ) , 避免“短串是长串前缀却误判相等”(如  "hello"  和  "helloworld" )。
  • < 的最终判断 return i2 < s._size(若前 min(_size, s._size) 个字符相同 , 短串更小) , 逻辑正确。

2.6 全局流运算符与 swap 

这部分接口是  string  支持 IO 操作和全局交换的关键 , 实现细节考虑周到。
 
1. 输出运算符(operator<<)

ostream& operator<<(ostream& out, const string& s)
{//out << s.c_str();for (size_t i = 0; i < s.size(); i++){out << s[i];}return out;
}
  • 实现:循环打印 s[i] (从 0 到 s.size()-1) , 而非直接打印 s.c_str()。
  • 优点:即使 _str 中包含 \0 (如二进制数据) , 也能打印到 size() 长度 , 更灵活;若仅打印文本 , 效果与 c_str() 一致。

 2. 输入运算符(operator>>)

istream& operator>>(istream& in, string& s)
{s.clear();char buff[128];int i = 0;char ch = in.get();while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == 127){buff[i] = '\0';s += buff;i = 0;}ch = in.get();}if (i > 0){buff[i] = '\0';s += buff;}return in;
}
  • 优化点
  • 先 s.clear() 清空原有内容 , 避免追加旧数据。
  • 用 char buff[128] 作为临时缓冲区 , 避免每次读一个字符就扩容(减少 reserve 调用次数 , 提升性能)。
  • 跳过空格和换行(符合 cin >> s 的默认行为:不读空白字符)。
  • 正确性:无内存越界 , 缓冲区满时及时追加到 s , 逻辑正确。

 3. getline  函数

	istream& getline(istream& in, string& s, char delim){s.clear();char buff[128];int i = 0;char ch = in.get();while (ch != delim){buff[i++] = ch;if (i == 127){buff[i] = '\0';s += buff;i = 0;}ch = in.get();}if (i > 0){buff[i] = '\0';s += buff;}return in;}
  • 逻辑:与 operator>> 类似 , 但终止符是 delim(默认 \n ) , 且不跳过空白字符(符合 getline 读取整行的行为)。
  • 正确性:解决了 operator>> 无法读取空格的问题 , 实现正确。

4. 全局 swap 函数

void swap(string& x, string& y)
{x.swap(y);
}
  • 实现:直接调用  x.swap(y) (成员函数 swap ) , 符合标准库“全局函数调用成员函数”的设计,确保交换逻辑统一(避免全局函数与成员函数逻辑不一致)。

2.3 总结

这份实现文件核心逻辑正确 , 异常安全 , 代码风格简洁 , 完全能支撑 string 的日常使用(如字符串增删查改、IO 操作、比较)。

感谢大家的观看!

http://www.dtcms.com/a/348973.html

相关文章:

  • 【论文阅读 | arXiv 2025 | WaveMamba:面向RGB-红外目标检测的小波驱动Mamba融合方法】
  • 上科大解锁城市建模新视角!AerialGo:从航拍视角到地面漫步的3D城市重建
  • 深度剖析Spring AI源码(三):ChatClient详解,优雅的流式API设计
  • R60ABD1 串口通信实现
  • 在 Ubuntu 24.04 或 22.04 LTS 服务器上安装、配置和使用 Fail2ban
  • 【Qwen Image】蒸馏版与非蒸馏版 评测小结
  • 第3篇:配置管理的艺术 - 让框架更灵活
  • 多线程下单例如何保证
  • [身份验证脚手架] 前端认证与个人资料界面
  • 2025.8.18-2025.8.24第34周:有内耗有挣扎
  • Spring Cloud 快速通关之Sentinel
  • 遥感机器学习入门实战教程|Sklearn案例⑩:降维与分解(decomposition 模块)
  • [e3nn] 等变神经网络 | 线性层o3.Linear | 非线性nn.Gate
  • 动态规划--编译距离
  • AI代码生成器全面评测:六个月、500小时测试揭示最强开发助手
  • Redis 高可用篇
  • 51单片机-实现定时器模块教程
  • GaussDB 数据库架构师修炼(十八) SQL引擎-统计信息
  • 用 WideSearch 思路打造「零幻觉、全覆盖」的多 Agent 信息收集器
  • SRE 系列(四)| MTTI 与 On-Call:高效故障响应之道
  • C++标准库算法:从零基础到精通
  • Go语言 Hello World 实例
  • 数据标注的质检环节有多少种
  • 单表查询-分析函数的应用
  • 智能体之推理引擎(3)
  • 记一次使用 C++ 实现多种扑克牌逻辑
  • ptrade `get_fundamentals` - 获取财务数据
  • 58 C++ 现代C++编程艺术7-模板友元
  • VC2022连接mysql
  • 微服务-21.网关路由-路由属性