C++之 String 类的模拟实现

本文只简述string类模拟实现的重点,其余不再过多赘述

一、模拟实现string类的构造函数

本文主要实现下图两个构造函数,即string()和string(const string& str)

而关于string的底层,其实就是数组,在物理逻辑上是连续的空间:

//string.h文件
namespace mxj
{class string{public://不带参的构造函数,编译器默认生成的已经满足使用需求//无参的构造,就是字符串'\0',所以在string的模拟实现里,带参的构造函数包含了无参构造函数//string();//带参构造函数string(const char* str = "");//给缺省值"",//这样我们的mxj::string s1就不会报错,不然得写成mxj::string s1()private:char* _str;size_t _size;size_t _capacity;};
}

 带参构造函数如下,以strlen为基础,计算开辟新空间的大小,通过开辟新的内存空间并将str字符串长度的下一个位置赋值'\0'

这样也很巧妙的解决了无参构造函数。

//string.cpp文件
namespace mxj
{string::string(const char* str):_str(new char[strlen(str) + 1])//这里为什么要加1?因为strlen不会计算\0, _size(strlen(str)), _capacity(strlen(str)){strcpy(_str, str);}
}

二、模拟实现string类的拷贝构造和赋值拷贝

拷贝构造和赋值拷贝可以通过交换函数来实现,拷贝构造和赋值拷贝都是深拷贝!!!

/ s2(s1),拷贝构造string::string(const string& s){_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}// s3=s2=s1=s,赋值拷贝,string& string::operator=(const string& s){if (this != &s){//先把原来的空间释放了,如果s1=s,s1原本的空间非常大,s的空间非常小,就非常容易造成空间浪费delete[] _str;//为什么要+1,因为还有\0,_size和_capacity都是不计算\0的_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}return *this;}

当然,上面有关拷贝构造和赋值拷贝虽然直观,但是很啰嗦 ,通过swap函数我们也能实现

两者均不改变当前对象的资源,都是通过临时对象进行资源的交换

//string.cpp
namespace mxj
{string::string(const string& s){string tmp(s._str);//将s中的资源构造对象tmpswap(tmp);}string& string::operator=(string s){swap(s);//临时对象s和*this进行交换资源return *this;}void swap(string& s1, string& s2){s1.swap(s2);//通过库函数中的swap来实现}void string::swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}
}

三、模拟实现string类的析构函数

析构函数需要清理类对象的资源

string.cpp
string::~string(){delete[] _str;_str = nullptr;_size = 0;_capacity = 0;}

 四、赋值运算符重载[ ]

赋值运算符重载[ ],使得string类有通过下标来访问字符串数据的功能

char& operator[](size_t i){return _str[i];}

五、获取字符串数据

string类底层是数组,接口直接返回字符串即可

const char* c_str() const{return _str;}

六、获取当前对象元素个数

size_t size() const{return _size;}

 七、清理当前string类对象的数据置空

字符串的终止符号为'\0',所以将字符串第一个元素置为字符'\0',字符串有效个数置零即可

void clear(){_str[0] = '\0';_size = 0;}

八、普通迭代器

迭代器是指针或者是像指针一样的东西,但在string类中,迭代器的底层是通过指针实现的

using iterator = char*;//using类似于typedef的作用
iterator begin()
{return _str;
}iterator end()
{return _str + _size;
}



九、const修饰的迭代器 

using const_iterator = const char*;
const_iterator begin()const
{return _str;
}const_iterator end()const
{return _str + _size;
}

十、string类一些常用接口的实现

    void string::reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];//保留n个位置实际上要保留n+1个,要给'\0'留位置strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;//_capacity是不计算'\0'的}}void string::push_back(char ch){if (_size == _capacity) {reserve(_capacity = 0 ? 4 : 2 * _capacity);}_str[_size] = ch;_size++;}void string::append(const char* str){size_t len = strlen(str);if (len + _size > _capacity){size_t newcapacity = 2 * _capacity;//如果扩了两倍还是不满足,那就按串的大小来扩if (len + _size >newcapacity){newcapacity = len + _size;}reserve(newcapacity);}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){//判断pos位置是否合法assert(pos <= _size);//判断是否需要扩容if (_size == _capacity) {reserve(_capacity == 0 ? 4 : 2 * _capacity);}//pos+1位置往后挪size_t end = _size;while (end > pos) {_str[end] = _str[end-1];--end;}_str[pos] = ch;_size++;}void string::insert(size_t pos, const char* str){size_t len = strlen(str);//判断是否需要扩容if (_size + len > _capacity) {size_t newCapacity = 2 * _capacity;if (newCapacity < _size + len){newCapacity = _size + len;}reserve(newCapacity);}//数据往后挪:str的长度为len,pos位置每一个字符都需要向后挪len个,size_t end = _size+len;while (end > pos + len-1) {_str[end] = _str[end-len];--end;}//插入for (size_t i = 0; i < pos; i++){_str[pos+i] = str[i];}//长度更新_size += len;}//删除void string::erase(size_t pos, size_t len){assert(pos < _size);if (len > _size - pos) {_str[pos] = '\0';_size = pos;}//挪数据else {size_t end = pos + len;while (end > pos) {_str[end-len] = _str[end];++end;}_size -= len;}}size_t string::find(char ch, size_t pos){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* ptr = strstr(pos+_str,str);if (ptr == nullptr){return npos;}else{return ptr - _str;}}string string::substr(size_t pos, size_t len){assert(pos < _size);if (len > (_size - pos)) {len = _size - pos;}mxj::string sub;sub.reserve(len);for (size_t i = pos; i < len; i++){sub += _str[pos + i];//尾插}return sub;}//通过复用bool operator== (const string& lhs, const string& rhs){return strcmp(lhs.c_str(), rhs.c_str()) == 0;}bool operator!= (const string& lhs, const string& rhs){return !(lhs == rhs);}bool operator> (const string& lhs, const string& rhs){return strcmp(lhs.c_str(), rhs.c_str()) > 0;}bool operator< (const string& lhs, const string& rhs){return !(lhs >=rhs);}bool operator>= (const string& lhs, const string& rhs){return (lhs > rhs)||(lhs==rhs);}bool operator<= (const string& lhs, const string& rhs){return !(lhs > rhs);}ostream& operator<<(ostream& os, const string& str){for (size_t i = 0; i < str.size(); i++){os << str[i];}return os;}istream& operator>>(istream& is, string& str){str.clear();char ch;//is >> ch;ch = is.get();while (ch != ' ' && ch != '\n')//如果想实现getline的效果while( ch != '\n'){str += ch;ch = is.get();}return is;}

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

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

相关文章

数据结构基础之《(9)—归并排序》

一、什么是归并排序 1、整体是递归&#xff0c;左边排好序右边排好序merge让整体有序 2、让其整体有序的过程里用了排外序方法 3、利用master公式来求解时间复杂度 4、当然可以用非递归实现 二、归并排序说明 1、首先有一个f函数 void f(arr, L, R) 说明&#xff1a;在arr上…

UIE与ERNIE-Layout:智能视频问答任务初探

内容来自百度飞桨ai社区UIE与ERNIE-Layout&#xff1a;智能视频问答任务初探&#xff1a; 如有侵权&#xff0c;请联系删除 1 环境准备 In [2] # 安装依赖库 !pip install paddlenlp --upgrade !pip install paddleocr --upgrade !pip install paddlespeech --upgrade In …

[代码随想录06]哈希表的使用,有效字母异位词,两数组交集,快乐数,两数之和

前言 哈希表是什么&#xff1f;一句话带你理解&#xff0c;简单来说我们对于杂乱的数据&#xff0c;怎么快速找到数据&#xff0c;如何做呢&#xff1f;一般的做法就是遍历复杂度为o(N)去找寻一个数据&#xff0c;但是吧&#xff0c;我们这样思考的话&#xff0c;还是花了大量时…

三维路径规划|基于黑翅鸢BKA优化算法的三维路径规划Matlab程序

三维路径规划|基于黑翅鸢BKA优化算法的三维路径规划Matlab程序 文章目录 前言三维路径规划|基于黑翅鸢BKA优化算法的三维路径规划Matlab程序基于黑翅鸢BKA优化算法的三维路径规划一、研究基本原理二、黑翅鸢BKA优化算法的基本步骤&#xff1a;三、详细流程四、总结 二、实验结果…

【问题】webdriver.Chrome()设置参数executable_path报不存在

场景1: 标红报错unresolved reference executable_path 场景2: 执行报错TypeError: __init__() got an unexpected keyword argument executable_path 原因&#xff1a; 上述两种场景是因为selenium4开始不再支持某些初始化参数。比如executable_path 解决&#xff1a; 方案…

Java - JSR223规范解读_在JVM上实现多语言支持

文章目录 1. 概述2. 核心目标3. 支持的脚本语言4. 主要接口5. 脚本引擎的使用执行JavaScript脚本执行groovy脚本1. Groovy简介2. Groovy脚本示例3. 如何在Java中集成 Groovy4. 集成注意事项 6. 与Java集成7. 常见应用场景8. 优缺点9. 总结 1. 概述 JSR223&#xff08;Java Spe…

无序抓取系列(一)

文章目录 一 摘要 二 资源 三 内容 一 摘要 最近&#xff0c;已经提出了许多抓取检测方法&#xff0c;这些方法可用于直接从传感器数据中定位机器人抓取配置&#xff0c;而无需估计物体姿态。其基本思想是将抓握感知视作计算机视觉中的对象检测。这些方法将噪声和部分遮挡的…

论文阅读:Deep divergence-based approach to clustering

论文地址&#xff1a;main.pdf (sciencedirectassets.com) 摘要 深度学习研究中的一个有前景的方向是通过优化判别损失函数&#xff0c;学习表示并同时发现无标签数据中的聚类结构。与监督式深度学习不同&#xff0c;这一研究方向尚处于起步阶段&#xff0c;如何设计和优化合适…

关于单片机的原理与应用!

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///计算机爱好者&#x1f60a;///目前正在学习C&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于单片…

Flink四大基石之CheckPoint(检查点) 的使用详解

目录 一、Checkpoint 剖析 State 与 Checkpoint 概念区分 设置 Checkpoint 实战 执行代码所需的服务与遇到的问题 二、重启策略解读 重启策略意义 代码示例与效果展示 三、SavePoint 与 Checkpoint 异同 操作步骤详解 四、总结 在大数据流式处理领域&#xff0c;Ap…

字典树TRIE

模板 模板总共分为两部分 插入一个字符串查找一个字符串 int idx 0; int trie[3000010][150]; int ans[3000010];##原理 trie[上节点编号][下方连接的字母] 下方连接的字母的节点编号 trie[0][0]1;trie[0][1]5; trie[1][1]2; trie[2][1]4;trie[2][2]3; trie[5][2]6; tri…

【MySQL-6】MySQL的复合查询

1. 整体学习的思维导图 2. 回顾基本查询 使用scott数据库中的表&#xff0c;完成以下查询&#xff1a; 查询工资高于500或岗位为MANAGER的雇员&#xff0c;同时还要满足他们的姓名首字母为大写的J mysql> select * from emp where (sal>500 or jobMANAGER) and ename …

STL算法之其它算法_中

目录 lower_bound(应用于有序区间) upper_bound&#xff08;应用于有序区间&#xff09; binary_search&#xff08;应用于有序区间&#xff09; next_permutation prev_permutation lower_bound(应用于有序区间) 这是二分查找(binary search)的一种版本&#xff0c;试图在…

[高阶数据结构六]最短路径算法

1.前言 最短路径算法是在图论的基础上讲解的&#xff0c;如果你还不知道图论的相关知识的话&#xff0c;可以阅读下面几篇文章。 [高阶数据结构四] 初始图论_初始图结构-CSDN博客 [高阶数据结构五] 图的遍历和最小生成树_图的遍历和生成树求解-CSDN博客 本章重点&#xff1a;…

uniapp:封装商品列表为组件并使用

封装商品列表为组件并使用 商品组件封装 <template><!-- 商品列表 --><view class"goods_list"><view class"goods_item" v-for"item in goods" :key"item.id"><image :src"item.img_url">…

【AI系统】LLVM 架构设计和原理

LLVM 架构设计和原理 在上一篇文章中&#xff0c;我们详细探讨了 GCC 的编译过程和原理。然而&#xff0c;由于 GCC 存在代码耦合度高、难以进行独立操作以及庞大的代码量等缺点。正是由于对这些问题的意识&#xff0c;人们开始期待新一代编译器的出现。在本节&#xff0c;我们…

【C语言】结构体(二)

一&#xff0c;结构体的初始化 和其它类型变量一样&#xff0c;对结构体变量可以在定义时指定初始值 #include <stdio.h> #include <stdlib.h> struct books // 结构体类型 {char title[50];char author[50]; //结构体成员char subject[100];int book_id; }…

四、初识C语言(4)

一、作业&#xff1a;static修饰局部变量 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <string.h> //作业&#xff1a;static修饰局部变量 int sum (int a) {int c 0;static int b 3;c 1;b 2;return (abc); } int main() {int i 0;int a …

Linux 中的 ls 命令:从使用到源码解析

ls 命令是 Linux 系统中最常用和最基本的命令之一。下面将深入探讨 ls 命令的使用方法、工作原理、源码解析以及实际应用场景。 1. ls 命令的使用** ls 命令用于列出目录内容&#xff0c;显示文件和目录的详细信息。 1.1 基本用法 ls [选项] [文件或目录]例如&#xff1a; …

The selected directory is not a valid home for Go SDK

在idea里配置go语言的环境时&#xff0c;选择go语言的安装目录&#xff0c;一直提示这个 The selected directory is not a valid home for Go SDK后来查了一下&#xff0c;发现原来idea识别不出来 需要改一下配置文件&#xff0c;找到go环境的安装目录&#xff0c;我是默认安…