C++之string的模拟实现

article/2025/8/1 13:56:43

string

  • 手写C++字符串类
      • 类的基本结构与成员变量
      • 一、构造函数与析构函数
      • 二、赋值运算符重载
      • 三、迭代器支持
      • 四、内存管理与扩容机制
      • 五、字符串操作函数
      • 六、运算符重载
      • 总结

在这里插入图片描述

手写C++字符串类

从零实现一个简易版std::string

类的基本结构与成员变量

namespace zzh {
class string {
private:char* _str;        // 存储字符串的字符数组size_t _size;      // 当前字符串长度size_t _capacity;  // 已分配的容量
public:static const size_t npos;  // 表示"不存在的位置"// 各类成员函数...
};
}

这个自定义字符串类主要通过动态分配的字符数组_str来存储字符串内容,并维护两个重要状态:_size表示当前字符串长度,_capacity表示已分配的内存容量。

  1. 基本成员变量
    在自定义 string 类中,我们需要定义一些基本的成员变量来存储字符串的内容和相关信息:
    _str:用于存储字符串的字符数组,通常是一个动态分配的 char 类型数组。
    _size:表示当前字符串的实际长度,不包括结尾的空字符 \0。
    _capacity:表示分配的内存容量,通常大于或等于 _size,用于优化内存分配效率。
  2. 构造函数和析构函数
    构造函数用于初始化 string 对象,常见的构造方式包括:
    从 C 风格字符串构造(const char*):通过 strlen 计算字符串长度,并动态分配内存来存储字符串内容。
    拷贝构造函数:用于从另一个 string 对象构造新对象,需要深拷贝内存以避免悬挂指针问题。
    默认构造函数:用于创建一个空字符串。
    析构函数则负责释放动态分配的内存,避免内存泄漏。
  3. 赋值运算符重载
    为了支持对象之间的赋值操作,我们需要重载赋值运算符 =。在实现时,需要注意自赋值的情况,并进行深拷贝以确保两个对象的内存独立。
  4. 内存管理
    字符串操作中,内存管理是一个关键问题。我们需要在字符串长度超过当前容量时动态扩展内存。通常的做法是将容量加倍,以减少频繁的内存分配操作。
  5. 迭代器支持
    为了方便遍历字符串中的字符,我们可以提供迭代器支持。通过定义 begin() 和 end() 方法,返回指向字符串首尾的指针,可以方便地使用标准库算法。
  6. 常见操作实现
    追加字符或字符串:通过 push_back 和 append 方法,可以在字符串末尾添加字符或另一个字符串的内容。在实现时,需要注意内存容量是否足够,并在必要时进行扩展。
    查找和替换:提供 find 方法用于查找字符或子字符串的位置,insert 和 erase 方法用于插入和删除字符或子字符串。
    比较操作:重载比较运算符(如 <、>、== 等),以便可以直接比较两个 string 对象的大小。

一、构造函数与析构函数

// 1. 从C风格字符串构造
string::string(const char* str)
{_size = strlen(str);_str = new char[_size + 1];_capacity = _size;memcpy(_str, str, _size + 1);
}// 2. 拷贝构造函数
string::string(const string& s)
{_size = s._size;_capacity = s._capacity;_str = new char[_capacity + 1];memcpy(_str, s._str, _size + 1);
}// 3. 析构函数
string::~string()
{delete[] _str;_str = nullptr;_size = 0;_capacity = 0;
}

关键点

  • 构造函数负责分配内存并复制字符串内容
  • 拷贝构造函数实现深拷贝,避免内存共享
  • 析构函数必须释放动态分配的内存,防止内存泄漏

二、赋值运算符重载

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;
}

技术要点

  • 使用临时变量确保异常安全
  • 自我赋值检查避免无效操作
  • 先分配新内存再释放旧内存,防止内存泄漏

三、迭代器支持

string::iterator string::begin() { return _str; }
string::iterator string::end() { return _str + _size; }

说明

  • 迭代器本质是字符指针
  • begin()返回字符串首地址
  • end()返回字符串末尾的下一个位置

四、内存管理与扩容机制

// 预分配内存
void string::reserve(size_t n)
{if (n > _capacity){char* tmp = new char[n + 1];memcpy(tmp, _str, _size + 1);delete[] _str;_str = tmp;_capacity = n;}
}// 追加字符
void string::push_back(char c)
{if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(newcapacity);}_str[_size++] = c;  // 注意:原代码此处错误地写入了固定字符'c'_str[_size] = '\0';
}

内存管理策略

  • 采用指数级扩容(2倍)减少内存分配次数
  • reserve()实现预分配,避免频繁扩容
  • 每次扩容后保留额外空间,提高插入效率

五、字符串操作函数

// 追加C风格字符串
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);}memcpy(_str + _size, str, len);  // 注意:原代码此处多复制了一个终止符_size += len;_str[_size] = '\0';  // 手动添加终止符
}// 查找字符
size_t string::find(char c, size_t pos = 0) const
{for (size_t i = pos; i < _size; i++){if (_str[i] == c)return i;}return npos;
}// 插入字符
string& string::insert(size_t pos, char c)
{assert(pos <= _size);if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(newcapacity);}// 从后向前移动元素for (size_t i = _size; i > pos; i--)_str[i] = _str[i - 1];_str[pos] = c;_size++;return *this;
}

核心算法

  • append()通过内存拷贝实现高效追加
  • find()线性查找目标字符
  • insert()通过元素后移实现插入操作
  • 使用memmove()处理内存重叠情况

六、运算符重载

// 比较运算符
bool string::operator<(const string& s)
{size_t i1 = 0, i2 = 0;while (i1 < _size && i2 < s._size){if (_str[i1] < s._str[i2])return true;else if (_str[i1] > s._str[i2])return false;i1++; i2++;}return i1 < s._size;  // 注意:原代码此处逻辑有误,已修正
}// 索引运算符
char& string::operator[](size_t index)
{assert(index < _size);return _str[index];
}

实现要点

  • 比较运算符按字典序逐字符比较
  • 索引运算符提供随机访问能力
  • 提供常量和非常量两个版本的重载

总结

通过手写这个简易版string类,我们深入理解了标准库字符串类的核心机制:动态内存管理、深拷贝实现、迭代器设计、扩容策略等。虽然现代C++编程中应优先使用std::string,但掌握这些底层原理有助于写出更高效、更安全的代码。


http://www.hkcw.cn/article/twawKKICBj.shtml

相关文章

Linux的调试器--gbd/cgbd

1.引入 #include <stdio.h> int Sum(int s, int e) {int result 0;for(int i s; i < e; i){result i;}return result; } int main() {int start 1;int end 100;printf("I will begin\n");int n Sum(start, end);printf("running done, result i…

云原生 Cloud Native Build (CNB)使用初体验

云原生 Cloud Native Build&#xff08;CNB&#xff09;使用初体验 引言 当“一切皆可云”成为趋势&#xff0c;传统开发环境正被云原生工具重塑。腾讯云CNB&#xff08;Cloud Native Build&#xff09;作为一站式开发平台&#xff0c;试图解决多环境协作难题。 本文将分享c…

硬件工程师笔记——运算放大电路Multisim电路仿真实验汇总

目录 1 运算放大电路基础 1.1 概述 1.1.1 基本结构 1.1.2 理想特性 1.2 运算放大分析方法 1.2.1 虚短 1.2.2虚断 1.2.3 叠加定理 2 同向比例运算放大电路 2.1 概述 2.1.1 基本电路结构 2.1.2 电路原理 2.2 仿真分析 2.2.1 电压增益 2.2.2 相位分析 3 反向比例运…

系统思考:经营决策沙盘

今年是我为黄浦区某国有油漆涂料企业提供经营决策沙盘培训的第二年。在这段时间里&#xff0c;我越来越感受到&#xff0c;企业的最大成本往往不在生产环节&#xff0c;而是在决策错误上所带来的长远影响。尤其是在如今这个复杂多变的环境下&#xff0c;企业面临的挑战愈发严峻…

Java线程:并发/并行区别、线程生命周期、乐观锁/悲观锁

并发、并行 进程 正在运行的程序(软件)就是一个独立的进程线程是属于进程的&#xff0c;一个进程中可以同时运行很多个线程进程中的多个线程其实是并发和并行执行的 并发 进程中的线程是由CPU负责调度执行的&#xff0c;但CPU能同时处理线程的数量有限&#xff0c;为了保证…

等保测评-Mysql数据库测评篇

Mysql数据库测评 0x01 前言 "没有网络安全、就没有国家安全" 等保测评是什么&#xff1f; 等保测评&#xff08;网络安全等级保护测评&#xff09;是根据中国《网络安全法》及相关标准&#xff0c;对信息系统安全防护能力进行检测评估的法定流程。其核心依据《信…

mysql的Memory引擎的深入了解

目录 1、Memory引擎介绍 2、Memory内存结构 3、内存表的锁 4、持久化 5、优缺点 6、应用 前言 Memory 存储引擎 是 MySQL 中一种高性能但非持久化的存储方案&#xff0c;适合临时数据存储和缓存场景。其核心优势在于极快的读写速度&#xff0c;需注意数据丢失风险和内存占…

QNAP MEMOS 域名访问 SSL(Lucky)

注意&#xff1a;下述是通过ssh、docker-compose方式安装docker的&#xff0c;不是直接在container station中安装的哈&#xff01;&#xff01;&#xff01; 一、编辑docker-compose.yml文件 用“#”号标识的&#xff0c;在保存文件的时候建议去掉&#xff0c;不然有时候会出…

BioID技术在宿主-病原体相互作用领域的应用

细菌感染是全球公共卫生的重大威胁&#xff0c;而抗生素耐药性的提升使我们迫切需要深入了解宿主 -病原体相互作用。细菌病原体通过分泌效应蛋白&#xff0c;操纵宿主细胞以建立感染。这些效应蛋白通过与宿主蛋白相互作用&#xff0c;改变宿主细胞功能&#xff0c;但传统研究方…

解析楼宇自控系统:分布式结构的核心特点与优势展现

在建筑智能化发展的进程中&#xff0c;楼宇自控系统作为实现建筑高效运行与管理的关键&#xff0c;其系统结构的选择至关重要。传统的集中式楼宇自控系统在面对日益复杂的建筑环境和多样化的管理需求时&#xff0c;逐渐暴露出诸多弊端&#xff0c;如可靠性低、扩展性差、响应速…

SAP Business One:无锡哲讯科技助力中小企业数字化转型的智慧之选

数字化转型&#xff0c;中小企业的必经之路 在当今竞争激烈的商业环境中&#xff0c;数字化转型已不再是大型企业的专利&#xff0c;越来越多的中小企业开始寻求高效、灵活的管理系统来优化业务流程、提升运营效率。作为全球领先的企业管理软件&#xff0c;SAP Business One…

Python基于Django的校园打印预约系统(附源码,文档说明)

博主介绍&#xff1a;✌IT徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3…

身份证发给别人怎么加水印?赛文奥特曼身份证添加水印教程

我们经常需要使用身份证照片进行身份验证、资料提交等操作。然而&#xff0c;直接将身份证照片发送给他人或上传到网络存在一定的信息泄露风险。为了更好地保护个人隐私&#xff0c;我们可以使用 简鹿水印助手 这款工具&#xff0c;在身份证照片上添加专属水印&#xff0c;从而…

Express教程【002】:Express监听GET和POST请求

文章目录 2、监听post和get请求2.1 监听GET请求2.2 监听POST请求 2、监听post和get请求 创建02-app.js文件。 2.1 监听GET请求 1️⃣通过app.get()方法&#xff0c;可以监听客户端的GET请求&#xff0c;具体的语法格式如下&#xff1a; // 1、导入express const express req…

ESP32-C3 Vscode+ESP-IDF开发环境搭建 保姆级教程

1.背景 最近esp32的芯片很火&#xff0c;因为芯片自带了WIFI和BLE功能&#xff0c;是物联网项目开发的首选芯片&#xff0c;所以&#xff0c;我也想搞个简单的esp32芯片试试看。于是&#xff0c;我设计了一个简单的板子。如下 这块板子很简单&#xff0c;主要的电路来自于乐鑫…

深入 RAG(检索增强生成)系统架构:如何构建一个能查资料的大语言模型系统

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《深度探秘&#xff1a;AI界的007》 &#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、前言 1、LLM 的局限&#xff1a;模型知识“封闭” vs 现实知识…

VSCode+Cline 安装配置及使用说明

安装配置 打开VSCode&#xff0c;点击左侧Extension图标&#xff0c;在弹出页面中&#xff0c;检索Cline&#xff0c;选择Cline进行安装。 安装完毕&#xff0c;在左侧会出现一个图标&#xff0c;点击图标 选择【Use your own API key】&#xff0c;在出来的界面中选择大模型&…

【征求意见】四川省大数据发展研究会关于对《数据资源建设费用测算标准》团体标准征求意见的通知

四川省大数据发展研究会 关于对《数据资源建设费用测算标准》团体标准征求意见的通知 各有关单位&#xff1a; 由四川省大数据发展研究会归口、成都东契奇科技有限公司牵头编制的《数据资源建设费用测算标准》团体标准已形成征求意见稿&#xff0c;现公开征求意见。请于2025年…

下载即转化的商业密码:解析华为应用商店CPD广告的智能投放逻辑

在移动互联网流量红利见顶的背景下&#xff0c;华为应用市场凭借其终端生态优势正成为开发者获客的新蓝海。数据显示&#xff0c;2025年Q1华为应用商店全球分发量同比增长27%&#xff0c;其中CPD广告因其"下载才付费"的精准特性&#xff0c;已成为金融、游戏、工具类…

Electron-vite【实战】MD 编辑器 -- 文件列表(含右键快捷菜单,重命名文件,删除本地文件,打开本地目录等)

最终效果 页面 src/renderer/src/App.vue <div class"dirPanel"><div class"panelTitle">文件列表</div><div class"searchFileBox"><Icon class"searchFileInputIcon" icon"material-symbols-light:…