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

较为深入的了解c++中的string类(2)

较为深入的了解c++中的string类(2)

那么我们现在接下来的学习主要就是通过复现string类中的一些函数, 也就是我们直接把string类中一些比较重要的东西写出来, 从创造string类的角度去更加深入的理解string中一些比较重要的逻辑.

注意:

这里不是完完全全的按照c++库里面的去仿写, 而是挑一些重点的逻辑功能写出来. 和库里面的string类是不同的. 只能说类似, 相似, 沿用库里面的设计思路大概的写出一个类似的.

概要:

string类, 其实就是我们之前学的数据结构, 尤其参考顺序表这个数据结构. 我们要复现的, 无非就是增删查改这戏操作, 以及c++中一些独特的逻辑, 比如构造函数, 析构函数, 以及一些重载.

这里我先将完整的代码贴出来给大家:

string.h

#include <cassert>
#include <iostream>
#include <cstring>
using namespace std;namespace lx
{class string{private:size_t _capacity = 0;size_t _size = 0;char* _str = nullptr;const static size_t npos = -1;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;}const char* c_str() const{return _str;}size_t size() const{return _size;}string(const char* str = "");string(const string& s);string& operator=(const string& s);~string();const char& operator[](size_t pos) const;char& operator[](size_t pos);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 insert(size_t pos, char ch);void insert(size_t pos, const char* str);void erase(size_t pos, size_t len = npos);void swap(string& s);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);void clear();};istream& operator>>(istream& in, string& s);ostream& operator<<(ostream& out, const string& s);
}

string.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include "string.h"namespace lx
{//注意:缺省值在函数声明的时候给上就好了//定义里就不要再写一次缺省值了//同样的,后面的缺省函数也是string::string(const char* str)//构造函数{_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);}//拷贝构造传统写法/*string::string(const string& s){_str = new char[s._capacity + 1];strcpy(_str, s._str);_capacity = s._capacity;_size = s._size;}*///现代拷贝构造string::string(const string& s){string tmp(s._str);swap(tmp);}//赋值重载传统写法string& string::operator=(const string& s){if (this != &s)//避免自己给自己赋值{char* tmp = new char[s._capacity + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;_capacity = s._capacity;_size = s._size;}return *this;}//赋值重载现代写法1/*string& string::operator=(const string& s){if (this != &s){string tmp(s);swap(tmp);			}return *this;}*///赋值重载现代写法2/*string& string::operator=(string s){swap(s);return *this;}*/string::~string()//析构函数{delete[] _str;_str = nullptr;_size = 0;_capacity = 0;}const char& string::operator[](size_t pos) const//[]重载(只读){//判断pos位置是否合法assert(pos <= _size);return _str[pos];}char& string::operator[](size_t pos)//[]重载(可读可写){//判断pos位置是否合法assert(pos <= _size);return _str[pos];}void string::reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void string::push_back(char ch){//判断空间是否足够插入新字符if (_size == _capacity)//按正常逻辑来说,_size是不会大于_capacity的{size_t newcapacity = _capacity == 0 ? 15 : _capacity * 2;reserve(newcapacity);}_str[_size] = ch;_size++;_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;}string& string::operator+=(char ch){push_back(ch);return *this;}string& string::operator+=(const char* str){append(str);return *this;}void string::insert(size_t pos, char ch){assert(pos <= _size);//判断空间是否足够插入新字符if (_size == _capacity)//按正常逻辑来说,_size是不会大于_capacity的{size_t newcapacity = _capacity == 0 ? 15 : _capacity * 2;reserve(newcapacity);}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 (_size + len > _capacity){reserve(_size + len);}int end = _size;while (end >= (int)pos){_str[end + len] = _str[end];--end;}strncpy(_str + pos, str, len);_size += len;/*size_t k = pos;for (size_t i = len; i > 0; i--){for (size_t end = _size + 1; end > k; end--){_str[end] = _str[end - 1];}_size++;k++;}strncpy(_str + pos, str,len);_size++;*/}void string::erase(size_t pos, size_t len){assert(pos <= _size);if (len == npos || pos + len >= _size){_size = pos;_str[pos] = '\0';}else{strcpy(_str + pos, _str + pos + len);_size -= len;/*for (size_t i = len; i > 0; i--){if (pos + len > _size)break;_str[pos] = _str[pos + len];pos++;}_size -= len;_str[_size] = '\0';*/}}void string::swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}size_t string::find(char ch, size_t pos){for (int 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* result = std::strstr(_str + pos, str);if (result != nullptr){return result - _str;//}else{return npos;}}string string::substr(size_t pos, size_t len){if (len == npos){len = _size - pos;}assert(pos+len <= _size);string tmp;for (size_t i = pos; i < pos+len; i++){tmp.push_back(_str[i]);}return tmp;}/*string string::substr(size_t pos, size_t len){assert(pos < _size);size_t end = pos + len;if (len == npos || pos + len >= _size){end = _size;}string str;str.reserve(end - pos);for (size_t i = pos; i < end; i++){str += _str[i];}return str;}*/void string::clear(){_size = 0;_str[0] = '\0';}istream& operator>>(istream& in, string& s){s.clear();char buff[128];char ch = in.get();int i = 0;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;}ostream& operator<<(ostream& out, const string& s){for (auto ch : s){out << ch;}return out;}
}

main.cpp//测试用的//我已经将框架搭好了,大家想要测试什么按照test1的模板写就ok了

#define _CRT_SECURE_NO_WARNINGS 1
#include "string.h"namespace lx
{void test1(){string s1("hello");cout << s1 << endl;}}
int main()
{lx:: test1();return 0;
}

讲解:

string类里的成员变量

在这里插入图片描述

迭代器的实现

在这里插入图片描述

看完迭代器的实现逻辑之后,大家可能会小瞧迭代器,觉得不过如此,不过这个东西设计出来肯定是有它的用处的,其次就是不是所有容器里面的迭代器都是如此实现的。不同容器中的迭代器是基于容器自身的特点来实现的,string类的迭代器能如此实现,主要就是根据string类的结构特点来设计的,因为string类其实就是用来管理字符数组的嘛,也可以看作是顺序表,他们底层的物理结构是连续的。啥意思?数组的存储在物理空间中是连续存储的,不会断断续续的像链表一样,像树一样。所以string类的迭代器看起来才如此简单(那是因为string类的底层物理结构就比较简单。)

范围for

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

构造函数

在这里插入图片描述

在这里插入图片描述

析构函数

在这里插入图片描述

swap函数

在这里插入图片描述

拷贝构造

在这里插入图片描述

在这里插入图片描述

赋值重载

在这里插入图片描述

由于现代赋值重载其实就是调用了拷贝构造,所以赋值重载现代写法和传统写法两种的区别就取决于你拷贝构造使用的是哪种写法,也就是说赋值重载的现代和传统的区别取决于拷贝构造。传统的赋值重载s1(现有对象),s2(被赋值对象)两种数据和内容是一样的,现代的赋值重载的结果取决于拷贝构造的写法,如果拷贝构造的写法是传统的,那么传统的赋值重载和现代的赋值重载没区别,如果拷贝构造函数用的是现代的,则传统的赋值重载和现代的赋值重载有可能有区别。_capacity可能会不一样。

[ ]重载

在这里插入图片描述

reserve函数

在这里插入图片描述

尾插(push_back)

在这里插入图片描述

append函数

在这里插入图片描述

+=重载

在这里插入图片描述

insert函数

在这里插入图片描述

在这里插入图片描述

erase函数

在这里插入图片描述

find函数

在这里插入图片描述

substr函数

在这里插入图片描述

clear函数

在这里插入图片描述

<<流插入重载

在这里插入图片描述

>>流提取重载

在这里插入图片描述

总结:

这一章我们主要复现了一些重要的操作函数,希望可以帮助到大家更好的理解string类。

http://www.lryc.cn/news/584799.html

相关文章:

  • Vue集成MarkDown
  • 在 React Three Fiber 中实现 3D 模型点击扩散波效果
  • CSS和CSS3区别对比
  • 【深度学习新浪潮】什么是AI个性化医疗?
  • 黑马点评系列问题之P55优惠券秒杀 快捷键问题 Ctrl+D显示不出来老师给的界面
  • 【数据结构】8. 二叉树
  • FastAPI + SQLAlchemy (异步版)连接数据库时,对数据进行加密
  • React Three Fiber 实现 3D 模型点击高亮交互的核心技巧
  • Gin 中常见参数解析方法
  • 用TensorFlow进行逻辑回归(二)
  • 闲庭信步使用图像验证平台加速FPGA的开发:第九课——图像插值的FPGA实现
  • 硬件加速(FPGA)
  • BigFoot Decursive 2.7.28 2025.07.11
  • MyBatis插件机制揭秘:从拦截器开发到分页插件实战
  • 深入剖析 ADL:C++ 中的依赖查找机制及其编译错误案例分析
  • Linux面试问题-软件测试
  • RISC-V:开源芯浪潮下的技术突围与职业新赛道 (二) RISC-V架构深度解剖(上)
  • idea如何打开extract surround
  • 【C++】——类和对象(上)
  • Linux指令与权限
  • Navicat实现MySQL数据传输与同步完整指南
  • python正则表达式(小白五分钟从入门到精通)
  • Vue 中监测路由变化时,通常不需要开启深度监听(deep: true)
  • Spring事务管理深度解析:原理、实践与陷阱
  • STM32-ADC
  • squash压缩合并
  • 计算机视觉速成 之 概述
  • 【学习笔记】机器学习(Machine Learning) | 第七章|神经网络(2)
  • Linux:库的原理
  • (C++)任务管理系统(文件存储)(正式版)(迭代器)(list列表基础教程)(STL基础知识)