C++ 中 string 类的解析及简易自我实现

目录

引言

标准库中的 string 类

功能概述

常见操作示例

自我实现简易 string 类

代码结构概述

1. String11.h 头文件

 类的成员变量

迭代器相关 

构造函数和析构函数 

 基本访问和修改方法

赋值运算符重载 

内存管理和扩容 

以下代码在.cpp文件中解析: 

 2. String11.cpp 实现文件

字符串修改操作

插入和删除操作 

 查找和截取操作

比较运算符重载 

输入输出运算符重载

3. Teststring.cpp 测试文件 

 

主要功能点

4.特色总结 

总结


引言

在 C++ 编程中,string 类是一个非常常用且重要的类,它位于标准库 <string> 中,为我们处理字符串提供了便捷的方式。不过,为了更好地理解 string 类的工作原理,我们可以尝试自己实现一个简易的 string 类。本文将详细解析 C++ 中的 string 类,并结合提供的代码文件,介绍如何自我实现一个简易的 string 类。

标准库中的 string 类

功能概述

C++ 标准库中的 string 类是一个模板类 std::basic_string<char> 的特化版本,它封装了字符数组,提供了一系列操作字符串的方法,如字符串的拼接、查找、替换、插入、删除等。使用 string 类,我们可以避免手动管理内存,减少内存泄漏的风险。

常见操作示例

#include <iostream>
#include <string>int main() {std::string s1 = "Hello";std::string s2 = " World";std::string s3 = s1 + s2; // 字符串拼接std::cout << s3 << std::endl;size_t pos = s3.find("World"); // 查找子字符串if (pos != std::string::npos) {std::cout << "Found at position: " << pos << std::endl;}s3.insert(5, ","); // 插入字符std::cout << s3 << std::endl;return 0;
}

自我实现简易 string 类

代码结构概述

提供的代码文件包含了三个主要部分:String11.h 头文件、String11.cpp 实现文件和 Teststring.cpp 测试文件。下面我们将详细分析每个部分。

1. String11.h 头文件

一些短小且常用的函数都可以直接在头文件内实现,调用时直接内联展开。

 类的成员变量
private:char* _str = nullptr;size_t _size;size_t _capacity;static const size_t npos;
  • _str:一个指向字符数组的指针,用于存储字符串的实际内容。
  • _size:表示当前字符串的长度,即字符串中字符的个数(不包括字符串结束符 '\0')。
  • _capacity:表示当前分配给字符串的内存容量,即 _str 指向的字符数组的大小(包括字符串结束符 '\0')。
  • npos:一个静态常量,表示无效的位置,通常用于表示查找操作未找到目标时的返回值。
迭代器相关 
public:typedef char* iterator;typedef const char* const_iterator; iterator begin(){return _str;}iterator end(){return _str + _size;}const_iterator begin() const{return _str;}const_iterator end() const{return _str + _size;}
  • 功能:定义了迭代器类型 iterator 和 const_iterator,并提供了 begin() 和 end() 方法,使得该 string 类可以像标准容器一样使用迭代器进行遍历。
  • 特点:支持范围 for 循环和其他基于迭代器的算法,增强了代码的通用性和可维护性。
构造函数和析构函数 
string(const char* str = "")
{_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);
}string(const string& s)
{_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;
}~string()
{delete[] _str;_str = nullptr;_size = _capacity = 0;
}
  • 默认构造函数:接受一个 C 风格字符串作为参数,若未提供则默认为空字符串。分配足够的内存来存储字符串,并将其复制到 _str 中。
  • 拷贝构造函数:接受一个 string 对象作为参数,创建一个新的 string 对象,其内容和容量与原对象相同。
  • 析构函数:释放 _str 指向的动态分配内存,避免内存泄漏,并将 _size 和 _capacity 置为 0。
 基本访问和修改方法
const char* c_str() const
{return _str;
}void clear()
{_str[0] = '\0';_size = 0;
}const size_t size() const
{return _size;
}const size_t capacity() const
{return _capacity;
}char& operator[](size_t pos)
{assert(pos < _size);return _str[pos];
}const char& operator[](size_t pos) const
{assert(pos < _size);return _str[pos];
}
  • c_str():返回指向字符串实际内容的 C 风格字符串指针。
  • clear():清空字符串,将字符串的第一个字符置为 '\0',并将 _size 置为 0。
  • size():返回字符串的当前长度。
  • capacity():返回字符串当前分配的内存容量。
  • operator[]:重载了下标运算符,允许通过下标访问字符串中的字符。提供了常量和非常量版本,以支持对常量和非常量对象的访问。
赋值运算符重载 
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;
}
  • 功能:实现了对象之间的赋值操作。如果目标对象和源对象不是同一个对象,释放目标对象的原有内存,分配新的内存并复制源对象的内容。
  • 特点:使用了自我赋值检查,避免了不必要的内存释放和重新分配。
内存管理和扩容 
void reserve(size_t n)
{cout << "reserve:" << n << endl;if(n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}
}
  • 功能:预留至少 n 个字符的内存空间。如果 n 大于当前容量,则分配新的内存,将原字符串复制到新内存中,并释放原内存。
  • 特点:避免了频繁的内存重新分配,提高了性能。
以下代码在.cpp文件中解析: 
void push_back(char ch);
void append(const char* str);
string& operator+=(char ch);
string& operator+=(const char* str);
void insert(size_t pos, char ch);
void insert(size_t pos, const char* str);
void erase(size_t pos, size_t len = npos);
size_t find(char ch, size_t pos = 0);
size_t find(const char* str, size_t pos = 0);
string substr(size_t pos = 0, size_t len = npos);
bool operator<(const string& s1, const string& s2);
bool operator<=(const string& s1, const string& s2);
bool operator>(const string& s1, const string& s2);
bool operator>=(const string& s1, const string& s2);
bool operator==(const string& s1, const string& s2);
bool operator!=(const string& s1, const string& s2);
istream& getline(istream& in, string& s);
ostream& operator<<(ostream& out, const string& s);
istream& operator>>(istream& in, string& s);

 2. String11.cpp 实现文件

先给定static变量npos值:

const size_t string::npos = -1;

 接下来实现剩下的代码:

字符串修改操作
void string::push_back(char ch){if(_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = ch;_size++;_str[_size] = '\0';}void string::append(const char* str){size_t len = strlen(str);if(len + _size > _capacity){// 大于2倍,需要多少开多少,小于2倍按2倍扩reserve(len + _size > _capacity * 2 ? len + _size : _capacity * 2);}strcpy(_str + _size, str);_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):在字符串末尾添加一个字符。如果当前容量不足,则进行扩容。
  • append(const char* str):在字符串末尾添加一个 C 风格字符串。如果需要,会进行扩容。
  • operator+=(char ch) 和 operator+=(const char* str):重载了 += 运算符,分别用于添加单个字符和 C 风格字符串,调用 push_back 和 append 方法实现。
插入和删除操作 
 void string::insert(size_t pos, char ch){assert(pos <= _size);if(_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}size_t end =  _size + 1;while(end > pos){_str[end] = _str[end - 1];end--;}_str[pos] = ch;_size++;}void string::insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);if(len + _size > _capacity){reserve(len + _size > 2 * _capacity ? len + _size : _capacity * 2);}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;}void string::erase(size_t pos, size_t len){assert(_size > pos);if(len >= _size - pos) // 不能写pos + len , 因为len == npos时为INT_MAX越界{_size = pos;_str[pos] = '\0';}else{size_t res = pos + len;while(res <= _size){_str[pos++] = _str[res++];}_size -= len;}}
  • insert(size_t pos, char ch):在指定位置 pos 插入一个字符。如果容量不足,会进行扩容。
  • insert(size_t pos, const char* str):在指定位置 pos 插入一个 C 风格字符串。同样,若需要会进行扩容。
  • erase(size_t pos, size_t len = npos):从指定位置 pos 开始删除 len 个字符。如果 len 大于等于从 pos 到字符串末尾的字符数,则删除从 pos 开始的所有字符。
 查找和截取操作
size_t string::find(char ch, size_t pos){assert(pos < _size);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){assert(pos < _size);const char* res = strstr(_str + pos, str);if(res == nullptr){return npos;}return res - _str;}string string::substr(size_t pos, size_t len){assert(pos < _size);//len大于字符串长度,更新一下lenif(len > _size - pos){len = _size - pos;}string sub;sub.reserve(len);for(size_t i = 0; i < len; i++){sub += _str[i + pos];}return sub;}
  • find(char ch, size_t pos = 0):从指定位置 pos 开始查找字符 ch,返回其第一次出现的位置。如果未找到,则返回 npos
  • find(const char* str, size_t pos = 0):从指定位置 pos 开始查找 C 风格字符串 str,返回其第一次出现的位置。若未找到,返回 npos
  • substr(size_t pos = 0, size_t len = npos):从指定位置 pos 开始截取长度为 len 的子字符串。如果 len 大于从 pos 到字符串末尾的字符数,则截取到字符串末尾。
比较运算符重载 
bool operator<(const string& s1, const string& s2){return strcmp(s1.c_str(), s2.c_str()) < 0;}bool operator<=(const string& s1, const string& s2){return strcmp(s1.c_str(), s2.c_str()) <= 0;}bool operator>(const string& s1, const string& s2){return !(s1 <= s2);}bool operator>=(const string& s1, const string& s2){return !(s1 < s2);}bool operator==(const string& s1, const string& s2){return strcmp(s1.c_str(), s2.c_str()) == 0;}bool operator!=(const string& s1, const string& s2){return !(s1 == s2);}
  • 功能:重载了比较运算符,用于比较两个 string 对象的大小。使用 strcmp 函数进行比较。
  • 特点:使得 string 对象可以像标准类型一样进行比较,方便进行排序和条件判断。
输入输出运算符重载
ostream& operator<<(ostream& out, const string& s){for(auto ch : s){out << ch;}return out;}// istream& operator>>(istream& in, string& s)// {//     s.clear();//     char ch;//     ch = in.get();//     while(ch != ' ' && ch != '\n')//     {//         s += ch;//         ch = in.get();//     }//     return in;// }优化istreamistream& operator>>(istream& in, string& s){s.clear();const int N = 256;char buff[N];int i = 0;char ch;ch = in.get();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;}istream& getline(istream& in, string& s){s.clear();const int N = 256;char buff[N];int i = 0;char ch;ch = in.get();while(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;}
  • getline(istream& in, string& s):从输入流中读取一行字符串,直到遇到换行符 '\n',并将其存储到 s 中。
  • operator<<(ostream& out, const string& s):重载了输出运算符,将 string 对象的内容输出到输出流中。
  • operator>>(istream& in, string& s):重载了输入运算符,从输入流中读取一个单词(以空格或换行符分隔),并将其存储到 s 中。

3. Teststring.cpp 测试文件 

 

#include "String11.h"namespace lmr
{void string_test(){string s("haha");cout << s.c_str() << endl;s += "sss";s += 's';cout << s.c_str() << endl;cout << s.size() << " " << s.capacity() <<  endl;s.insert(2, "sss");cout << s.c_str() << endl;s.insert(5, '*');cout << s.c_str() << endl;s.erase(2);cout << s.c_str() << endl;string s1("test.cpp");size_t pos = s1.find('.');string buffix = s1.substr(pos);cout << buffix.c_str() << endl;   }void string_test1(){string s("Hello World");string s2(s);bool sign = s2 == s;if(sign == true){// cin >> s;getline(cin, s);cout << s << endl;string s3;cin >> s3;cout << s3 << endl;}cout << endl;}}int main()
{lmr::string_test1();return 0;
}
主要功能点
  • 测试用例:提供了多个测试用例,测试了字符串的拼接、插入、删除、查找、截取等操作。

4.特色总结 

  1. 动态内存管理:通过 new 和 delete 运算符动态分配和释放内存,避免了固定大小数组的限制。
  2. 模拟标准库接口:提供了与 std::string 类似的接口,如迭代器、构造函数、赋值运算符等,方便用户使用。
  3. 内存优化:使用 reserve 方法进行内存预分配,减少了频繁的内存重新分配,提高了性能。
  4. 异常处理:使用 assert 进行边界检查,确保操作的安全性。但在实际应用中,建议使用更健壮的异常处理机制。
  5. 可扩展性:可以方便地添加更多的功能,如替换、大小写转换等。

总结

通过自我实现一个简易的 string 类,我们可以更深入地理解 C++ 中 string 类的工作原理和内存管理机制。同时,我们也学习了如何使用类和对象的概念,以及如何重载运算符和使用迭代器。在实际编程中,建议使用标准库中的 string 类,因为它经过了严格的测试和优化,具有更高的性能和可靠性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/87983.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/87983.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机的性能指标(选择题0~1题无大题)

存储器的性能指标 总容量存储单元个数*存储字长 bit 例&#xff1a;MAR16位&#xff0c;MDR16位 总容量2的16次方*16bit 补充&#xff1a; n个二进制位就有2的n次方不同的状态 一般描述文件大小容量单位 2的10次方&#xff1a;K 2的20次方&#xff1a;M 2的…

React 核心原理与Fiber架构

目录 一、虚拟 DOM 二、Diffing 算法 三、Fiber 架构 四、渲染流程 1. Render 阶段&#xff08;可中断异步过程&#xff09; 2. Commit 阶段&#xff08;同步不可中断&#xff09; 五、时间切片&#xff08;Time Slicing&#xff09; 六、核心流程步骤总结 1. 状态更新…

【破局痛点,赋能未来】领码 SPARK:铸就企业业务永续进化的智慧引擎—— 深度剖析持续演进之道,引领数字化新范式

摘要 在瞬息万变的数字时代&#xff0c;企业对业务连续性、敏捷创新及高效运营的需求日益迫切。领码 SPARK 融合平台&#xff0c;秉持“持续演进”这一核心理念&#xff0c;以 iPaaS 与 aPaaS 为双擎驱动&#xff0c;深度融合元数据驱动、智能端口调度、自动化灰度切换、AI 智…

掌握C++核心特性

目标&#xff1a; 掌握C核心特性&#xff0c;为嵌入式开发打基础 好的&#xff0c;我来为你详细梳理一下 继承与多态、虚函数 相关的知识点&#xff0c;包括单继承、多继承、虚函数表机制、纯虚函数与抽象类、动态绑定。以下内容适合中等难度层次的理解&#xff0c;便于考试复…

python的高校教师资源管理系统

目录 技术栈介绍具体实现截图系统设计研究方法&#xff1a;设计步骤设计流程核心代码部分展示研究方法详细视频演示试验方案论文大纲源码获取/详细视频演示 技术栈介绍 Django-SpringBoot-php-Node.js-flask 本课题的研究方法和研究步骤基本合理&#xff0c;难度适中&#xf…

Java Collections工具类:高效集合操作

Collections工具类概述 Collections是Java提供的集合操作工具类&#xff0c;位于java.util包中&#xff0c;包含大量静态方法&#xff0c;用于对List、Set、Map等集合进行排序、查找、替换、同步化等操作。 常用方法及代码示例 排序操作 sort(List<T> list)&#xff1a…

vue指令总结

vue指令总结 一、总述 二、代码实现&#xff08;内含大量注释&#xff09; <!DOCTYPE html> <html> <head><meta charset"utf-8"><title>vue入门</title><!-- 使用Vue 3官方CDN --><script src"https://unpkg.c…

RUP——统一软件开发过程

RUP概述 RUP&#xff08;Rational Unified Process&#xff09;&#xff0c;统一软件开发过程&#xff0c;统一软件过程是一个面向对象且基于网络的程序开发方法论。 在RUP中采用“41”视图模型来描述软件系统的体系结构。“41”视图包括逻辑视图、实现视图、进程视图、部署视…

SpringBoot电脑商城项目--增加减少购物车商品数量

1. 持久层 1.1 规划sql语句 执行更新t_cart表记录的num值根据cid查询购物车的数据是否存在 select * from t_cart where cid#{cid} 1.2 接口和抽象方法 /*** 获取购物车中商品的数据总数* return 购物车中商品的数据总数*/Cart findByCid(Integer cid); 1.3 xml文件中sql映射…

零基础学习Redis(13) -- Java使用Redis命令

上期我们学习了如何使用Java连接到redis&#xff0c;这期我们来学习如何在java中使用redis中的一些命令 1. set/get 可以看到jedis类中提供了很多set方法 public static void test1(Jedis jedis) {jedis.flushAll();jedis.set("key1", "v1");jedis.set(&q…

解决OSS存储桶未创建导致的XML错误

前言 在Java开发中&#xff0c;集成对象存储服务&#xff08;OSS&#xff09;时&#xff0c;开发者常会遇到一个令人困惑的错误提示&#xff1a; “This XML file does not appear to have any style information associated with it. The document tree is shown below.” 此…

Spring 表达式语言(SpEL)深度解析:从基础到高级实战指南

目录 一、SpEL是什么&#xff1f;为什么需要它&#xff1f; 核心价值&#xff1a; 典型应用场景&#xff1a; 二、基础语法快速入门 1. 表达式解析基础 2. 字面量表示 3. 属性访问 三、SpEL核心特性详解 1. 集合操作 2. 方法调用 3. 运算符大全 4. 类型操作 四、Sp…

算法导论第二十四章 深度学习前沿:从序列建模到创造式AI

第二十四章 深度学习前沿&#xff1a;从序列建模到创造式AI 算法的进化正在重新定义人工智能的边界 深度学习作为机器学习领域最活跃的分支&#xff0c;正以惊人的速度推动着人工智能的发展。本章将深入探讨五大前沿方向&#xff0c;通过原理分析、代码实现和应用场景展示&…

抽象工厂设计模式

1.问题背景&#xff1a; 现在有两个产品(Product)分别是手机壳(PhoneCase)和耳机(EarPhone)&#xff0c;但是他们会来自于各个生产厂商&#xff0c;比如说Apple和Android等等 那么至少会有四个产品&#xff0c;分别是安卓手机壳&#xff0c;安卓耳机&#xff0c;苹果手机壳&a…

GESP 3级 C++ 知识点总结

根据GESP考试大纲 (2024年3月版)&#xff0c;帮大家总结一下GESP 3级 C语言的知识点&#xff1a; 核心目标&#xff1a; 掌握C程序的基本结构&#xff0c;理解并能运用基础的编程概念解决稍复杂的问题&#xff0c;重点是函数、一维数组和字符串处理。 主要知识点模块&#x…

腾讯云主动型云蜜罐技术解析:云原生威胁狩猎的革新实践(基于腾讯云开发者社区技术网页与行业实践)

摘要 腾讯云主动型云蜜罐&#xff08;Active Cloud Honeypot&#xff09;通过动态诱捕机制和云原生架构&#xff0c;在威胁检测效率、攻击链还原深度、防御联动实时性等维度实现突破。相较于传统蜜罐&#xff0c;其核心优势体现在&#xff1a; 部署效率&#xff1a;分钟级完成…

企业微信wecom/jssdk的使用(入门)

概述 记录一个企业微信jssdk的使用&#xff0c;因为要用到图片上传、扫描二维码等工具。项目是uniapp开发的h5项目&#xff0c;fastadmin&#xff08;thinkphp5&#xff09;后端 先看官方文档 https://developer.work.weixin.qq.com/document/path/90547#%E5%BA%94%E7%94%A8…

大零售生态下开源链动2+1模式、AI智能名片与S2B2C商城小程序的协同创新研究

摘要&#xff1a;在流量红利消退、零售形态多元化的背景下&#xff0c;大零售生态成为商业发展的新趋势。本文聚焦开源链动21模式、AI智能名片与S2B2C商城小程序在零售领域的协同应用&#xff0c;探讨其如何打破传统零售边界&#xff0c;实现流量变现与用户资产化。研究表明&am…

Scrapy全流程(一)

创建一个scrapy项目:scrapy startproject mySpider 生成一个爬虫:scrapy genspider douban movie.douban.com 提取数据:完善spider&#xff0c;使用xpath等方法 保存数据:pipeline中保存数据 2 创建scrapy项目 下面以抓取豆瓣top250来学习scrapy的入门使用&#xff1a;豆瓣…

【Elasticsearch】TF-IDF 和 BM25相似性算法

在 Elasticsearch 中&#xff0c;TF-IDF 和 BM25 是两种常用的文本相似性评分算法&#xff0c;但它们的实现和应用场景有所不同。以下是对这两种算法的对比以及在 Elasticsearch 中的使用情况&#xff1a; TF-IDF - 定义与原理&#xff1a;TF-IDF 是一种经典的信息检索算法&am…