C++ string的模拟实现

Hello!!大家早上中午晚上好,昨天复习了string的使用,今天来模拟实现一下string!!!

一、string的框架搭建

1.1首先我们需要一个string的头文件用来做变量、函数、类等声明;再需要一个test文件来做测试,还需要一个文件来实现方法;

包一下头文件ok初步工作完成;

1.2明确string需要用到的成员变量以及成员函数

成员变量:

①必不可少的一个指向字符串的char*指针;(注意这里不能设置为const char*)

②字符串存储的有效数据size_t size;(有效数据指的时'\0'之前的数据)

③字符串存储的那块空间的容量size_t capacity;

④一个无符号最大值npos(用来判断);

成员函数:

①string的构造函数;包括用字符串构造、用string对象构造、用n个字符构造、用迭代器区间构造;实现常用的几个即可;

②string的析构函数;(因为涉及深拷贝所以要写)

③string的运算符重载函数;包括赋值运算符重载,比较小于、大于、等于、不等于运算重载函数,还有下标访问的运算符重载函数,流插入流提取运算符重载函数;

普通成员函数:

①开空间并初始化的 resize函数;

②开空间不初始化的reserve函数;

③插入单个字符的push_back函数;

④插入字符串的+=运算符重载函数;

⑤某个位置插入n个字符的insert函数;

⑥某个位置删除n个字符的erase函数;

⑦获取string有效数据的size()函数;

⑧获取string存储空间大小的capacity()函数;

⑨清理字符串的clear函数;

⑩从某个位置开始查找某个字符的find函数;

11、从某个位置开始截取长度为len的字符串;

12、获取_str的c_str函数;

还有一个需要用到的是迭代器;

1.3声明到头文件

由于模拟实现的string类的名字会跟std里的string冲突,所以需要namespace一个命名空间;

#pragma once
#include <iostream>
using namespace std;
//string的模拟实现
namespace ldc
{struct string{typedef char* iterator;typedef const char* const_iterator;public:string(const char* str);string(const string& s);string(size_t n, char c);string(const_iterator it1, const_iterator it2);~string();string& operator=(const string& s);bool operator>(const string& s)const;bool operator<(const string& s)const;bool operator==(const string& s)const;bool operator!=(const string& s)const;bool operator >= (const string & s)const;bool operator <=(const string& s)const;char operator[](size_t pos);const char operator[](size_t pos)const;void resize(size_t n, char c='\0');void reserve(size_t n);void push_back(char c);string& operator+=(const char* str);string& opeartor += (const string & s);string& ldc::string::operator+=(char ch);void insert(size_t pos, size_t n, char c);void erase(size_t pos, size_t n=npos);size_t size();size_t capacity();void clear();size_t find(char c, size_t pos = 0);size_t find(const char* str, size_t pos = 0);string& substr(size_t pos = 0, size_t len = npos);const char* c_str const();private:char* _str;size_t _size;size_t _capacity;const static size_t npos=-1;};ostream& operator<<(ostream& out, const string& s);//流提出插入重载istream& operator>>(istream& in, string& s);//在类外重载不存在this
}

这里注意:npos用来做判断,当调用erase在pos位置删除n个字符时,如果不传参n默认为npos(无符号最大值),表示从pos位置开始往后的全删除;(同理substr也一样);

二、开始实现

2.1构造函数的实现
 #define _CRT_SECURE_NO_WARNINGS 1
#include "string3_20.h"
#include <string.h>
//string的方法实现
//构造函数
ldc::string::string(const char* str="")
{size_t len = strlen(str);_size = len;_capacity = _size;_str = new char[_capacity + 1];//这里+1因为要保留一个位置存放'\0',空串也有'\0'memcpy(_str, str, len + 1);
}
ldc::string::string(const string& s)
{_size = s._size;_capacity = s._capacity;_str = new char[_capacity + 1];memcpy(_str, s._str, _capacity + 1);
}
ldc::string::string(size_t n, char c)
{_size = n;_capacity = _size;_str = new char[_capacity + 1];for (size_t i = 0; i < n; i++){_str[i] = c;}_str[n] = '\0';
}
ldc::string::string(const_iterator it1, const_iterator it2)
{_size = it2 - it1;_capacity = _size;_str = new char[_capacity + 1];memcpy(_str, it1, _size);_str[_size] = '\0';
}

顺便把流插入重载和c_str也实现了(好测试)

const char* ldc::string::c_str()const
{return _str;
}
ostream& ldc::operator<<(ostream& out, const string& s)
{out <<s.c_str();return out;
}

测试:

 #define _CRT_SECURE_NO_WARNINGS 1
//string的测试
#include "string3_20.h"
int main()
{ldc::string s1("hello world!!"); //字符串构造const char* ch = "welcome";  ldc::string s2(ch);cout << s1 << endl;cout << s2 << endl;char ch2[10] = { "yes!!!" };ldc::string s3(ch2, ch2 + 3);//区间构造cout << s3 << endl;ldc::string s4(s3);//拷贝构造cout<<s4<<endl;return 0;
}

ok没问题!接下来实现以下赋值重载跟析构;

2.2赋值重载与析构函数
ldc::string& ldc::string::operator=(const string& s)
{_size = s._size;_capacity = s._capacity;_str = new char[_capacity + 1];memcpy(_str, s._str, _capacity + 1);return *this;
}
ldc::string::~string()
{delete[] _str;_str = nullptr;_size = _capacity = 0;
}

测试:

int main()
{ldc::string s1("hello");ldc::string s2("welcome");s1 = s2;cout << s1 << endl;return 0;
}

没问题,接下来实现普通成员函数;

2.3普通成员函数的实现
//比较、下标访问运算符重载
bool ldc::string::operator>(const string& s)const
{//比较有很多种方法,可以用operator[]比也可以获取str后再比,这里用最简洁的方法//库里的memcmp内存比较函数int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);//先比短return ret == 0 ? _size > s._size:ret > 0;//再比长,如果相等ret为正数表示str1>str2
}
bool ldc::string::operator<=(const string& s)const
{return !(*this > s);
}
bool ldc::string::operator<(const string& s)const
{return !(*this >= s);
}
bool ldc::string::operator>=(const string& s)const
{return (*this > s) || (*this == s);
}
bool ldc::string::operator==(const string& s)const
{return _size == s._size && memcmp(_str, s._str, _size) == 0;
}
bool ldc::string::operator!=(const string& s)const
{return !(*this == s);
}char ldc::string::operator[](size_t pos)
{assert(pos < _size);return _str[pos];
}
const char ldc::string::operator[](size_t pos)const
{assert(pos < _size);return _str[pos];
}

测试:

void test1()
{ldc::string s1("hello");ldc::string s2("helloxxx");ldc::string s3("welcome");ldc::string s4("welcome");cout << (s1 > s2) << " ";cout << (s1 >= s2) << " ";cout << (s1 < s2) << " ";cout << (s1 <= s2) << " ";cout << (s3 == s4) << " ";cout << (s3!= s2) << " ";cout << s1[2] << " ";s1[0]='Y';cout << s1 << " ";//const ldc::string s5("beutiful!");//s5[0] = 'B';
}
int main()
{test1();return 0;
}

OK没问题接着实现扩容:

2.4扩容实现
//只开空间不初始化
void ldc::string::reserve(size_t n)
{if (n > _capacity){char* tmp = new char[n + 1];//假设开100个空间,需要存放100个数据,第101个位置需存放结束标识符'\0'memcpy(tmp, _str, _size + 1);//连'\0'一起拷贝,不然开完空间后的数据没有结束标志符delete[] _str;//释放旧空间_str = tmp;//指向新空间_capacity = n;//注意只需要修改_capacity,_size有效数据始终没变}
}//开空间并初始化
void ldc::string::resize(size_t n, char c = '\0')
{if (n < _size){_size = n;_str[_size] = '\0';}else{reserve(n);for (size_t i=_size;i<n;i++){_str[i] = c;}_str[n] = '\0';_size = n;}
}

测试:

void test2()
{ldc::string s1("hello");s1.resize(20, 'x');cout << s1 << endl;s1.resize(3);cout << s1 << endl;
}
int main()
{//test1();test2();return 0;
}

ok没问题下一步,reserve后面测;

2.5增删查改实现
void ldc::string::push_back(char c)
{if (_size == _capacity){size_t n = _capacity == 0 ? 4 : _capacity * 2;//二倍扩容reserve(n);}_str[_size] = c;++_size;_str[_size] = '\0';
}
ldc::string& ldc::string::operator+=(const char* str)
{size_t len = strlen(str);reserve(_size + len);//复用reserve开空间memcpy(_str + _size, str, len + 1);//'\0'一起拷贝过去_size += len;return *this;
}
ldc::string& ldc::string::operator+=(char ch)
{push_back(ch);return *this;
}
ldc::string&ldc::string::operator+= (const string & s)
{reserve(_size + s._size);memcpy(_str + _size, s._str, s._size + 1);_size += s._size;return *this;
}
void ldc::string::insert(size_t pos, size_t n, char c)
{assert(pos <= _size);reserve(_size + n);if (pos==_size)//尾插{while (n--){push_back(c);}}else{char* begin = _str + _size;char* newbegin = _str + pos;char* end = _str + _size + n;while (begin>=newbegin){*end = *begin;--end;--begin;}int i = n;while (i--){_str[pos] = c;++pos;}_size += n;}}
void ldc::string::erase(size_t pos, size_t n)
{assert(pos < _size);if (n == npos || pos + n > _size){_str[pos] = '\0';}else{size_t end = _size;size_t begin = pos + n;while (begin != end){_str[pos] = _str[begin];++pos; ++begin;}}_size -= n;_str[_size] = '\0';
}
size_t ldc::string::find(char c, size_t pos)
{assert(pos < _size);while (pos != _size){if (_str[pos] == c){return pos;}++pos;}return npos;
}
size_t ldc::string::find(const char* str, size_t pos)
{//直接用strstr函数char* ptr = strstr(_str + pos, str);if (ptr){return ptr - _str;}elsereturn npos;
}

测试:

void test3()
{ldc::string s1("hello world!!");s1.insert(5, 3, 'X');cout << s1 << endl;s1.insert(0, 3, 'Y');cout << s1 << endl;s1.insert(19, 2, 'Z');cout << s1 << endl;s1.erase(0, 3);cout << s1 << endl;s1.erase(5, 4);cout << s1 << endl;s1.erase(10, -1);cout << s1 << endl;cout << s1.find('h')<<endl;cout << s1.find("world") << endl;cout << s1.find('l', 3) << endl;
}

没啥问题,接着把剩下的都实现了;

三、剩余完善

3.1substr实现
ldc::string ldc::string::substr(size_t pos, size_t len)
{assert(pos < _size);size_t n = len;if (n == npos || pos + n > _size){n = _size - pos;}string temp;temp.reserve(n);for (size_t i = 0; i < n; i++){temp += _str[pos + i];}return temp;
}
3.2其他实现
size_t ldc::string::size()const
{return _size;
}
size_t ldc::string::capacity()const
{return _capacity;
}
void ldc::string::clear()
{_str[0] = '0';_size = 0;
}
istream& ldc::operator>>(istream& in, string& s)
{s.clear();char ch = in.get();// 处理前缓冲区前面的空格或者换行while (ch == ' ' || ch == '\n'){ch = in.get();}//in >> ch;char buff[128];int i = 0;while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == 127){buff[i] = '\0';s += buff;i = 0;}//in >> ch;ch = in.get();}if (i != 0){buff[i] = '\0';s += buff;}return in;
}
3.3测试
void test4()
{ldc::string s1("hello");ldc::string s2(s1.substr(0, 2));cout << s1 << endl;cout << s2 << endl;cout <<"开空间前:" << s2.capacity() <<" ," << s2.size() << endl;s2.reserve(40);cout <<"开空间后:"<< s2.size() << ", " << s2.capacity() << endl;cin>>s2;cout << s2<<endl;cout << "流插入后:" << s2.size() << " " << s2.capacity() << endl;
}

OK!!到这里string的基本功能已经实现了!!如果您觉得有所收获,记得点赞收藏+关注哦!!谢谢!!!

咱下期见!!!

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

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

相关文章

Java 中装饰者模式与策略模式在埋点系统中的应用

前言 在软件开发中&#xff0c;装饰者模式和策略模式是两种常用的设计模式&#xff0c;它们在特定的业务场景下能够发挥巨大的作用。本文将通过一个实际的埋点系统案例&#xff0c;探讨如何在 Java 中运用装饰者模式和策略模式&#xff0c;以及如何结合工厂方法模式来优化代码…

【3-22 list 详解STL C++ 】

先看代码&#xff0c;常用的就是代码中有的那些 #include <bits/stdc.h> using namespace std; int main() {list<int> mylist;for(int i0;i<5;i){mylist.push_back(i);//TODO}for(const auto&i:mylist)cout<<i<<\n;//fanzhuanreverse(mylist.…

田间机器人幼苗视觉检测与护苗施肥装置研究(大纲)

田间机器人幼苗视觉检测与护苗施肥装置研究 基于多光谱视觉与精准施肥的农业机器人系统设计 第一章 绪论 1.1 研究背景与意义 农业智能化需求&#xff1a; 传统幼苗检测依赖人工&#xff0c;效率低且易遗漏弱苗/病苗施肥不精准导致资源浪费和环境污染 技术挑战&#xff1a;…

如何在Linux CentOS上安装和配置Redis

如何在Linux CentOS上安装和配置Redis 大家好&#xff0c;我是曾续缘。欢迎来到本教程&#xff01;今天我将向您介绍在Linux CentOS上安装和配置Redis的详细步骤。Redis是一个高性能的键值存储系统&#xff0c;常用于缓存、消息队列和数据持久化等应用场景。让我们一起开始吧&…

requests库post方法怎么传params类型的参数

在使用 requests 库的 post 方法时&#xff0c;params 类型的参数通常用于在 URL 中作为查询字符串传递。这与 data 或 json 参数不同&#xff0c;后者是放在请求体中的。下面详细介绍如何在使用 post 方法时传递 params 参数。 使用 params 参数 params 参数接受一个字典或包…

C++常见问题与思考

TLS&#xff08;线程本地存储&#xff09;原理 线程本地存储&#xff08;Thread Local Storage&#xff0c;TLS&#xff09;是一种机制&#xff0c;它允许每个线程拥有自己独立的变量实例&#xff0c;这些变量的生命周期与线程相同。也就是说&#xff0c;不同线程对同一个 TLS…

如何快速下载并安装 Postman?

从下载、安装、启动 Postman 这三个方面为大家详细讲解下载安装 Postman 每一步操作&#xff0c;帮助初学者快速上手。 Postman 下载及安装教程(2025最新)

使用Gitee Go流水线部署个人项目到服务器指南

使用Gitee Go流水线部署个人项目到服务器指南 前言&#xff01;&#xff01;&#xff01; 本文解决的问题&#xff1a; 你有一台ECS服务器&#xff0c;你在上面部署了一个Java服务也就是一个jar&#xff0c;你觉着你每次手动本地打包&#xff0c;上传&#xff0c;在通过命令去…

Linux第一节:Linux系统编程入门指南

摘要 本文面向Linux初学者&#xff0c;系统讲解操作系统核心概念、Shell命令实战、权限管理精髓及目录结构解析。通过思维导图命令示例原理解析的方法&#xff0c;帮助开发者快速构建Linux知识体系&#xff0c;掌握生产环境必备技能。 一、Linux的前世今生&#xff1a;从实验室…

【Linux 维测专栏 5 -- linux pstore 使用介绍】

文章目录 Linux pstore 功能简介1. pstore 概述2. pstore 的核心功能3. pstore 的工作原理4. pstore 的使用示例5. pstore 的优势6. 典型应用场景配置示例1)DTS配置2)config配置运行测试及log问题小结Linux pstore 功能简介 1. pstore 概述 pstore(Persistent Storage)是…

在 ASP .NET Core 9.0 中使用 Scalar 创建漂亮的 API 文档

示例代码&#xff1a;https://download.csdn.net/download/hefeng_aspnet/90407900 Scalar 是一款可帮助我们为 API 创建精美文档的工具。与感觉有些过时的默认 Swagger 文档不同&#xff0c;Scalar 为 API 文档提供了全新而现代的 UI。其简洁的设计让开发人员可以轻松找到测试…

Rabbitmq消息被消费时抛异常,进入Unacked 状态,进而导致消费者不断尝试消费(下)

一、消费流程图 消息在消费出现异常的时候&#xff0c;将一直保留在消息队列&#xff0c;所以你会看到以下奇怪的现象&#xff1a; 消息队列仅有5个消息&#xff0c; 投递速度也非常快&#xff0c;结果却一直无法消费掉。 二、重试策略 重试机制的使用场景&#xff1a;重试机制…

【STM32】知识点介绍二:GPIO引脚介绍

文章目录 一、概述二、GPIO的工作模式三、寄存器编程 一、概述 GPIO&#xff08;英语&#xff1a;General-purpose input/output&#xff09;,即通用I/O(输入/输出)端口&#xff0c;是STM32可控制的引脚。STM32芯片的GPIO引脚与外部设备连接起来&#xff0c;可实现与外部通讯、…

JavaScript流程控制精讲(二)运算符与循环实战

JavaScript流程控制精讲&#xff08;二&#xff09;运算符与循环实战 学习目标&#xff1a;掌握条件判断与循环控制&#xff0c;实现基础业务逻辑 核心要点&#xff1a;运算符优先级 | 短路运算 | 循环优化 | 项目实战 一、运算符进阶技巧 1.1 算术运算符 console.log(5 % 3)…

如何在IPhone 16Pro上运行python文件?

在 iPhone 16 Pro 上运行 Python 文件需要借助第三方工具或远程服务&#xff0c;以下是具体实现方法和步骤&#xff1a; 一、本地运行方案&#xff08;无需越狱&#xff09; 使用 Python 编程类 App 以下应用可在 App Store 下载&#xff0c;支持直接在 iPhone 上编写并运行 …

【赵渝强老师】达梦数据库的数据库对象

达梦数据库中包含各种数据库对象&#xff0c;主要分为两大类型&#xff1a;基本数据库对象和复杂数据库对象。下面分别进行介绍。 视频讲解如下 【赵渝强老师】达梦数据库的数据库对象 一、 基本数据库对象 常见的基本数据库对象有&#xff1a;表、索引、视图、序列、同义词等…

【每日算法】Day 6-1:哈希表从入门到实战——高频算法题(C++实现)

摘要 &#xff1a;掌握高频数据结构&#xff01;今日深入解析哈希表的核心原理与设计实现&#xff0c;结合冲突解决策略与大厂高频真题&#xff0c;彻底掌握O(1)时间复杂度的数据访问技术。 一、哈希表核心思想 哈希表&#xff08;Hash Table&#xff09; 是一种基于键值对的…

LeetCode 第29题、30题

LeetCode 第29题&#xff1a;两数相除 题目描述 给你两个整数&#xff0c;被除数dividend和除数divisor。将两数相除&#xff0c;要求不使用乘法、除法和取余运算。整数除法应该向零截断&#xff0c;也就是截去其小数部分。例如&#xff0c;8.345将被截断为8&#xff0c;-2.733…

26考研——树与二叉树_树、森林(5)

408答疑 文章目录 二、树、森林树的基本概念树的定义和特性树的定义树的特性 基本术语树的基本术语和概念祖先、子孙、双亲、孩子、兄弟和堂兄弟结点的层次、度、深度和高度树的度和高度分支结点和叶结点有序树和无序树路径和路径长度 森林的基本术语和概念森林的定义森林与树的…

【HarmonyOS Next之旅】DevEco Studio使用指南(六)

目录 1 -> 在模块中添加Ability 1.1 -> Stage模型添加UIAbility 1.1.1 -> 在模块中添加UIAbility 1.1.2 -> 在模块中添加Extension Ability 2 -> 创建服务卡片 2.1 -> 概述 2.2 -> 使用约束 2.3 -> 创建服务卡片 2.4 -> 创建动态/静态卡片…