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

手写一个C++字符串类:从底层理解String的实现

这是我通过自己动手实现一个字符串类,希望能够让你更好地理解C++的类设计、内存管理和运算符重载等概念。

基本结构设计

首先我们来看一下字符串类的基本框架:

namespace yzq {class String {private:char* _str;         // 指向动态分配的字符数组size_t _size;       // 当前字符串长度size_t _capacity;   // 当前容量static const size_t npos;  // 表示无效位置};
}

这里我们使用动态分配的字符数组来存储字符串,_size记录当前字符串的实际长度,_capacity记录当前分配的内存容量。

构造函数和析构函数

构造函数

String(const char* s1 = "") {_size = strlen(s1);_str = new char[_size + 1];  // 多分配一个位置存放'\0'_capacity = _size;strcpy(_str, s1);
}

构造函数接收一个C风格字符串,默认是空字符串。我们使用strlen获取字符串长度,然后动态分配足够的内存(记得要多分配一个位置给结束符'\0'),最后用strcpy拷贝内容。

拷贝构造函数

String(const String& s1) {char* tmp = new char[s1._capacity + 1];strcpy(tmp, s1._str);_str = tmp;_size = s1._size;_capacity = s1._capacity;
}

拷贝构造函数实现深拷贝,先创建新内存,再拷贝内容,最后更新成员变量。

析构函数

~String() {delete[] _str;_str = nullptr;
}

析构函数负责释放动态分配的内存,避免内存泄漏。

内存管理: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;}
}

reserve函数用于提前分配足够的内存。只有当请求的大小大于当前容量时才进行扩容,这样可以避免频繁的内存重新分配。

字符串操作函数

尾插字符 push_back

void String::push_back(char s1) {if (_size == _capacity) {reserve(_capacity == 0 ? 4 : 2 * _capacity);  // 2倍扩容}_str[_size++] = s1;_str[_size] = '\0';
}

在尾部插入字符时,先检查容量是否足够,不够就进行2倍扩容,然后插入字符并更新大小。

追加字符串 append

void String::append(const char* s1) {size_t len = strlen(s1);if (_size + len > _capacity) {reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);}strcpy(_str + _size, s1);_size += len;
}

追加字符串时,先计算需要追加的长度,如果空间不够就扩容(采用更智能的扩容策略:需要的大小超过2倍容量就按需分配,否则按2倍扩容),然后用strcpy拷贝内容。

运算符重载 +=

String& String::operator+=(char s1) {push_back(s1);return *this;
}String& String::operator+=(const char* s1) {append(s1);return *this;
}

+=运算符重载直接复用push_backappend函数,让代码更简洁。

插入和删除操作

插入 insert

void String::insert(size_t pos, char s1) {assert(pos <= _size);if (_size == _capacity) {reserve(_capacity == 0 ? 4 : 2 * _capacity);}size_t end = _size + 1;while (end > pos) {_str[end] = _str[end - 1];end--;}_str[pos] = s1;_size++;
}

插入字符时,先把从插入位置开始的字符都向后移动一位,腾出位置后再插入新字符。

删除 erase

void String::erase(size_t pos, size_t len) {assert(pos < _size);if (len == npos || pos + len >= _size) {_str[pos] = '\0';_size = pos;}else {for (size_t i = pos + len; i <= _size; i++) {_str[i - len] = _str[i];}_size -= len;}
}

删除操作分两种情况:如果要删除到字符串末尾,直接在删除位置设置结束符;否则把后面的字符向前移动覆盖要删除的部分。

查找和子串

查找 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 String::npos;
}size_t String::find(const char* s, size_t pos) const {assert(pos < _size);char* pos_ptr = strstr(_str + pos, s);if (pos_ptr == nullptr) {return String::npos;}return pos_ptr - _str;
}

查找字符使用遍历,查找子串使用strstr函数,找到后通过指针相减计算位置。

获取子串 substr

String String::substr(size_t pos, size_t len) const {assert(pos < _size);String s1;if (pos + len > _size) {len = _size - pos;}s1.reserve(len);for (size_t i = 0; i < len; i++) {s1 += _str[i + pos];}return s1;
}

获取子串时先处理长度越界的情况,然后预分配内存,最后逐个字符追加。

输入输出重载

流输出 operator<<

ostream& operator<<(ostream& out, const String& s) {for (auto ch : s) {out << ch;}return out;
}

使用范围for循环遍历字符串输出每个字符。

流输入 operator>>

istream& operator>>(istream& in, String& s) {char ch = in.get();const int N = 256;char buff[N];int i = 0;while (ch != ' ' && ch != '\n') {buff[i++] = ch;if (i == N - 1) {buff[i] = '\0';s += buff;i = 0;}ch = in.get();}if (i > 0) {buff[i] = '\0';s += buff;}return in;
}

这里使用缓冲区机制:先积累256个字符再一次性添加到字符串中,避免频繁扩容,提高效率。

比较运算符重载

bool String::operator==(const String& d) {return strcmp(_str, d._str) == 0;
}bool String::operator!=(const String& d) {return !(*this == d);
}// 其他比较运算符类似...

比较运算符都基于strcmp函数实现,注意!=可以复用==的实现。

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

相关文章:

  • 大学学院教授委员会制度研究(四)职能设置--杨立恒毕业论文
  • Docker 命令自动补全:临时与持久化配置指南
  • 简单使用Nest+Nacos+Kafka实现微服务
  • 了解学习Redis主从复制
  • 【含文档+PPT+源码】基于java web的篮球馆管理系统系统的设计与实现
  • 眉山建设银行官方网站html5的网站设计与实现是做什么
  • 【音视频】图像与音频的3A技术:ISP相机与音频3A算法的对比
  • 字节码的“字节”含义
  • 做天然文化石的网站锦州网站建设多少钱
  • HarmonyOS实战项目:打造智能家居控制中心(设备发现与控制)
  • Linux存储软件栈剖析之第5篇:F2FS文件系统
  • iis7 网站权限设置chromeseo是什么
  • 新网站建设服务在线crm视频在线crm
  • MongoDB入门指南基础篇
  • 【洛谷】高精度专题 加减乘除全实现
  • 6.1.1.1 大数据方法论与实践指南-Spark/Flink 任务开发规范
  • _金仓数据库平替MongoDB实战:制造业生产进度管理的国产化升级之路
  • java-learn(8):拼图小游戏
  • 建设银行 福建分行招聘网站山西城乡建设厅网站首页
  • STM32学习(MCU控制)(SysTick and TIM)
  • 【高并发服务器】十一、Acceptor监听套接字模块 LoopThreadPool线程池模块
  • uniapp vue3 点击跳转外部网页
  • 基于“开源AI智能名片链动2+1模式S2B2C商城小程序”的会员制培养策略研究
  • 做家居网站设计o2o型网站
  • IDEA推送github,身份认证错误:Cannot assign requested address: getsockopt 解决方法
  • Rust Actix-Web框架源码解析:基于Actor模型的高性能Web开发
  • LLM辅助轻量级MES编排系统低代码开发方案介绍
  • 网站国际网络备案号百度收录提交之后如何让网站更快的展示出来
  • 学习Linux——组管理
  • 文件批量重命名(办公)脚本