C++:模拟实现STL的string
目录
一.实现string类
1.string的构造及析构
2.string类的遍历
3.string类的插入和删除
4.string类的空间处理
5.string类的查找
6.string类的输出和输入
7.string类的常用判断
二.整体代码
1.string.h
2.string.cpp
一.实现string类
在前一节中我们了解了STL中string的部分接口,在这里我们实现一个自己的string类
当我们实现自己的string类时,名字会与库内的冲突,所以可以选择用命名空间解决
1.string的构造及析构
当我们在开空间的时候一般要多开一个用于存放\0
拷贝构造和赋值用传统方法写一般是开新空间,复制过去,释放旧空间,但是在这里我们使用现代写法,直接将空间交换过来使用
而在swap函数中使用::,是为了让他去调用库中的函数,而不是类中的
string(const char* str = "")
{_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];//strcpy(_str,str);不安全,会报错,无法实现功能,在后面主要使用strcpy_sstrcpy_s(_str, strlen(str) + 1, str);//c11标准
}//深拷贝,若用编译器自动生成的为浅拷贝,析构时会出现问题
string(const string& s):_str(nullptr),_size(0),_capacity(0)
{string tmp(s._str);//his->swap(tmp);swap(tmp);
}void swap(string& s)
{::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);
}~string()
{delete[] _str;_str = nullptr;_size = _capacity = 0;
}string& operator=(string s)
{//this->swap(s);swap(s);return *this;
}
2.string类的遍历
对于string类的遍历我们可以通过[]+下标,迭代器,范围for语句遍历,以及c_str
所以我们需要自己定义迭代器,重载[]
typedef char* iterator;iterator begin()
{return _str;
}iterator end()
{return _str + _size;
}char& operator[](size_t i)
{assert(i < _size);return _str[i];
}const char& operator[](size_t i) const
{assert(i < _size);return _str[i];
}const char* c_str()
{return _str;
}
3.string类的插入和删除
对于插入我们都要考虑的便是容量不够需要增容,同时也应该考虑字符串开始为空的情况,如果为空就给一个容量,否则就扩2倍
push_back的话我们只需要将字符加在末端即可
append将插入的字符串复制到原字符串末尾即可
+=对于单个push_back,字符串复用append
insert插入单个字符时,将字符从末尾开始向后移一位,将字符插入头即可,插入字符串时后移的位数变为插入字符串的长度
erase删除主要考虑两种情况,删除一部分和全部删完,全部删完比较简单,将删除的位置变为\0,将size变为pos即可,删除部分,将后面的字符前移覆盖
void push_back(char ch){if (_size == _capacity){size_t newcapacity = _capacity == 0 ? 2 : _capacity * 2;reserve(newcapacity);}_str[_size] = ch;++_size;_str[_size] = '\0';}void append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}//strcpy(_str + _size, str);strcpy_s(_str + _size, strlen(str) + 1, str);_size += len;}string& operator+=(char ch){this->push_back(ch);return *this;}string& operator+=(const char* str){this->append(str);return *this;}string& insert(size_t pos, char ch){assert(pos < _size);if (_size == _capacity){size_t newcapacity = _capacity == 0 ? 2 : _capacity * 2;reserve(newcapacity);}int end = _size;while (end >= (int)pos){_str[end + 1] = _str[end];--end;}_str[pos] = ch;++_size;return *this;}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;}for (size_t i = 0; i < len; ++i){_str[pos++] = str[i];}_size += len;return *this;}void erase(size_t pos, size_t len = npos){assert(pos < _size);if (len >= _size - pos){_str[pos] = '\0';_size = pos;}else{size_t i = pos + len;while (i <= _size){_str[i - len] = _str[i];++i;}_size -= len;}}
4.string类的空间处理
reserve比较简单,直接开出一段空间即可,而resize则需要考虑空间的缩小,缩小的话将给定位置变为\0,放大空间的话,reserve空间,然后将size后面的都填上给定的或默认字符
size_t size() const{return _size;}size_t capacity() const{return _capacity;}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];//strcpy(tmp, _str);strcpy_s(tmp, strlen(_str) + 1, _str);delete[] _str;_str = tmp;_capacity = n;}}void resize(size_t n, char ch = '\0'){if (n < _size){_str[n] = '\0';_size = n;}else{if (n > _capacity){reserve(n);}for (size_t i = _size; i < n; ++i){_str[i] = ch;}_size = n;_str[_size] = '\0';}}
5.string类的查找
size_t find(char ch, size_t pos = 0)
{for (size_t i = pos; i < _size; ++i){if (_str[i] == ch){return i;}}return npos;
}size_t find(const char* str, size_t pos = 0)
{char* p = strstr(_str, str);if (p == nullptr){return npos;}else{return p - _str;}
}
6.string类的输出和输入
ostream& operator<<(ostream& out, const string& s){for (size_t i = 0; i < s.size(); ++i){cout << s[i];}return out;}//getline()只需要去掉if中的' '即可istream& operator>>(istream& in, string& s){while (1){char ch;ch = in.get();if (ch == ' ' || ch == '\n'){break;}else{s += ch;}}return in;}
7.string类的常用判断
bool operator<(const string& s)
{int ret = strcmp(_str, s._str);return ret < 0;
}bool operator==(const string& s)
{int ret = strcmp(_str, s._str);return ret == 0;
}bool operator<=(const string& s)
{return *this < s || *this == s;
}bool operator>(const string& s)
{return !(*this <= s);
}bool operator>=(const string& s)
{return !(*this < s);
}bool operator!=(const string& s)
{return !(*this == s);
}
二.整体代码
1.string.h
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
namespace wzyl
{class string{public:typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}string(const char* str = ""){_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];//strcpy(_str,str);不安全,会报错,无法实现功能,在后面主要使用strcpy_sstrcpy_s(_str, strlen(str) + 1, str);//c11标准}//深拷贝,若用编译器自动生成的为浅拷贝,析构时会出现问题string(const string& s):_str(nullptr),_size(0),_capacity(0){string tmp(s._str);//his->swap(tmp);swap(tmp);}void swap(string& s){::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);}~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}string& operator=(string s){//this->swap(s);swap(s);return *this;}size_t size() const{return _size;}size_t capacity() const{return _capacity;}char& operator[](size_t i){assert(i < _size);return _str[i];}const char& operator[](size_t i) const{assert(i < _size);return _str[i];}const char* c_str(){return _str;}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];//strcpy(tmp, _str);strcpy_s(tmp, strlen(_str) + 1, _str);delete[] _str;_str = tmp;_capacity = n;}}void resize(size_t n, char ch = '\0'){if (n < _size){_str[n] = '\0';_size = n;}else{if (n > _capacity){reserve(n);}for (size_t i = _size; i < n; ++i){_str[i] = ch;}_size = n;_str[_size] = '\0';}}void push_back(char ch){if (_size == _capacity){size_t newcapacity = _capacity == 0 ? 2 : _capacity * 2;reserve(newcapacity);}_str[_size] = ch;++_size;_str[_size] = '\0';}void append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}//strcpy(_str + _size, str);strcpy_s(_str + _size, strlen(str) + 1, str);_size += len;}string& operator+=(char ch){this->push_back(ch);return *this;}string& operator+=(const char* str){this->append(str);return *this;}string& insert(size_t pos, char ch){assert(pos < _size);if (_size == _capacity){size_t newcapacity = _capacity == 0 ? 2 : _capacity * 2;reserve(newcapacity);}int end = _size;while (end >= (int)pos){_str[end + 1] = _str[end];--end;}_str[pos] = ch;++_size;return *this;}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;}for (size_t i = 0; i < len; ++i){_str[pos++] = str[i];}_size += len;return *this;}void erase(size_t pos, size_t len = npos){assert(pos < _size);if (len >= _size - pos){_str[pos] = '\0';_size = pos;}else{size_t i = pos + len;while (i <= _size){_str[i - len] = _str[i];++i;}_size -= len;}}size_t find(char ch, size_t pos = 0){for (size_t i = pos; i < _size; ++i){if (_str[i] == ch){return i;}}return npos;}size_t find(const char* str, size_t pos = 0){char* p = strstr(_str, str);if (p == nullptr){return npos;}else{return p - _str;}}bool operator<(const string& s){int ret = strcmp(_str, s._str);return ret < 0;}bool operator==(const string& s){int ret = strcmp(_str, s._str);return ret == 0;}bool operator<=(const string& s){return *this < s || *this == s;}bool operator>(const string& s){return !(*this <= s);}bool operator>=(const string& s){return !(*this < s);}bool operator!=(const string& s){return !(*this == s);}private:char* _str;size_t _size;//有效字符个数size_t _capacity;//能存多少有效字符,\0不是有效字符static size_t npos;};size_t string::npos = -1;ostream& operator<<(ostream& out, const string& s){for (size_t i = 0; i < s.size(); ++i){cout << s[i];}return out;}//getline()只需要去掉if中的' '即可istream& operator>>(istream& in, string& s){while (1){char ch;ch = in.get();if (ch == ' ' || ch == '\n'){break;}else{s += ch;}}return in;}void test_string1(){string s1("hello");string s2;cout << s1 << endl;cout << s2 << endl;cout << s1.c_str() << endl;cout << s2.c_str() << endl;//3种遍历方式for (size_t i = 0; i < s1.size(); ++i){s1[i] += 1;cout << s1[i] << " ";}cout << endl;string::iterator it = s1.begin();while (it != s1.end()){*it -= 1;cout << *it << " ";++it;}cout << endl;//范围for是由迭代器支持的,所以这段代码最后会被替换成迭代器for (auto e : s1){cout << e << " ";}cout << endl;}void test_string2(){string s1("hello");s1.push_back(' ');s1.push_back('w');s1.push_back('o');s1.push_back('r');s1.push_back('l');s1.push_back('d');cout << s1 << endl;s1.append("xxxxxxxxx");cout << s1 << endl;string s2;s2 += "hello";s2 += ' ';s2 += "world";cout << s2 << endl;}void test_string3(){string s1("hello");s1.insert(1, 'x');s1.insert(1, "xyz");s1.insert(0, 'p');cout << s1 << endl << endl;string s2("hello");s2.reserve(10);cout << s2 << endl;cout << s2.size() << endl;cout << s2.capacity() << endl << endl;s2.resize(10, 'x');cout << s2 << endl;cout << s2.size() << endl;cout << s2.capacity() << endl << endl;s2.resize(18, 'a');cout << s2 << endl;cout << s2.size() << endl;cout << s2.capacity() << endl << endl;s2.resize(2);cout << s2 << endl;cout << s2.size() << endl;cout << s2.capacity() << endl;}void test_string4(){string s("helloworld");s.erase(5, 2);cout << s << endl;s.erase(5, 4);cout << s << endl;string s1("abcdabcdef");cout << s1.find("cde") << endl;cout << s1.find("fgh") << endl;}void test_string5(){string s;cin >> s;cout << s << endl;string s1(s);cout << s1 << endl;string s2 = s1;cout << s2 << endl;}
}
2.string.cpp
#include "string.h"int main()
{wzyl::test_string1();wzyl::test_string2();wzyl::test_string3();wzyl::test_string4();wzyl::test_string5();return 0;
}
相关文章:
C++:模拟实现STL的string
目录 一.实现string类 1.string的构造及析构 2.string类的遍历 3.string类的插入和删除 4.string类的空间处理 5.string类的查找 6.string类的输出和输入 7.string类的常用判断 二.整体代码 1.string.h 2.string.cpp 一.实现string类 在前一节中我们了解了STL中stri…...
【Python TensorFlow】入门到精通
TensorFlow 是一个开源的机器学习框架,由 Google 开发,广泛应用于机器学习和深度学习领域。本篇将详细介绍 TensorFlow 的基础知识,并通过一系列示例来帮助读者从入门到精通 TensorFlow 的使用。 1. TensorFlow 简介 1.1 什么是 TensorFlow…...
数据结构:七种排序及总结
文章目录 排序一插入排序1直接插入排序2希尔排序二选择排序3直接选择排序4堆排序三 交换排序5冒泡排序6快速排序四 归并排序7归并排序源码 排序 我们数据结构常见的排序有四大种,四大种又分为七小种,如图所示 排序:所谓排序,就是…...
【安当产品应用案例100集】030-使用企业微信登录Windows,实现工作电脑与业务系统登录方式统一
随着越来越多的企业信息系统从intranet开放到internet,企业员工的办公接入方式也越发多样,信息系统面临的数据安全问题也呈现爆发的趋势。一些大企业,比如Google、Microsoft、Huawei有强大的开发能力、IT能力,可以构建出自己的零信…...
大数据数据存储层MemSQL, HBase与HDFS
以下是对 MemSQL、HBase 和 HDFS 的详细介绍,这些工具在分布式数据存储和处理领域有着重要作用。 1. MemSQL MemSQL(现称为 SingleStore)是一种分布式内存数据库,兼具事务处理(OLTP)和分析处理(OLAP)的能力,专为高性能实时数据处理设计。 1.1 核心特点 内存优先存储…...
【HarmonyOS】鸿蒙应用设置控件通用样式AttributeModifier, @Styles
【HarmonyOS】鸿蒙应用设置控件通用样式AttributeModifier, Styles 前言 在鸿蒙中UI开发经常需要对控件样式进行统一的封装,在API早前版本,一般是通过 Styles进行样式封装复用: Entry Component struct Index {build() {Column(…...
Scala IF...ELSE 语句
Scala IF...ELSE 语句 Scala 是一种多范式的编程语言,它结合了面向对象和函数式编程的特点。在 Scala 中,if...else 语句是一种基本且常用的控制结构,用于根据条件执行不同的代码块。与 Java 或 Python 等其他语言中的 if...else 语句类似&a…...
快速上手vue3+js+Node.js
安装Navicat Premium Navicat Premium 创建一个空的文件夹(用于配置node) 生成pakeage.json文件 npm init -y 操作mysql npm i mysql2.18.1 安装express搭建web服务器 npm i express4.17.1安装cors解决跨域问题 npm i cors2.8.5创建app.js con…...
06 网络编程基础
目录 1.通信三要素 1. IP地址(Internet Protocol Address) 2. 端口号(Port Number) 3. 协议(Protocol) 2.TCP与UDP协议 三次握手(Three-Way Handshake) 四次挥手(…...
Python 的 FastApi 如何在request 重复取request.body()
需求背景: 需要再中间件中获取body 中的信息 但是 又想要在之后 还可以重复取 这个body 因为有的接口写法是直接从body中获取参数,然而这个body是数据流的形式,一旦取一次就导致后面取不到里面的值了 。 解决方式: 1.保存请求体: 在中间件中读取请求…...
qt QFontDialog详解
1、概述 QFontDialog 是 Qt 框架中的一个对话框类,用于选择字体。它提供了一个可视化的界面,允许用户选择所需的字体以及相关的属性,如字体样式、大小、粗细等。用户可以通过对话框中的选项进行选择,并实时预览所选字体的效果。Q…...
AI时代,通才可能会占据更有利的地位
在AI时代,通才不仅有生存的可能,而且根据多个参考内容,他们实际上可能占据更有利的地位。以下几点解释了为什么通才在人工智能时代具有重要性和生存空间: 适应性和灵活性:通才因其广泛的知识基础和跨领域的技能&#x…...
qt QHeaderView详解
1、概述 QHeaderView 是 Qt 框架中的一个类,它通常作为 QTableView、QTreeView 等视图类的一部分,用于显示和管理列的标题(对于水平头)或行的标题(对于垂直头)。QHeaderView 提供了对这些标题的排序、筛选…...
探索PickleDB:Python中的轻量级数据存储利器
文章目录 探索PickleDB:Python中的轻量级数据存储利器1. 背景:为什么选择PickleDB?2. PickleDB是什么?3. 如何安装PickleDB?4. 简单的库函数使用方法创建和打开数据库设置数据获取数据删除数据保存数据库 5. 应用场景与…...
yocto下编译perf失败的解决方法
文章目录 问题分析库没有安装?文件缺少?解决参考问题 在新环境使用yocto编译镜像时,发现最后一直编译不过perf,具体的编译提示错误如下 ERROR: perf-1.0-r9 do_compile: oe_runmake failed ERROR: perf-1.0-r9 do_compile: Execution of /home/ub-1001/work/as66/imx8LBV…...
丹摩征文活动|详解 DAMODEL(丹摩智算)平台:为 AI 开发者量身打造的智算云服务
本文 什么是 DAMODEL(丹摩智算)?DAMODEL 的平台特性快速上手 DAMODEL 平台GPU 实例概览创建 GPU 云实例 储存选项技术支持与社区服务结语 在人工智能领域的飞速发展中,计算资源与平台的选择变得尤为重要。为了帮助 AI 开发者解决高…...
ORACLE _11G_R2_ASM 常用命令
--------查看磁盘组,(空间情况) select name,state,free_mb,total_mb,usable_file_mb from v$asm_diskgroup; --------查看磁盘情况 select GROUP_NUMBER,free_mb,total_mb,disk_number,MOUNT_STATUS,mode_status, HEADER_STATUS,name,PATH from v$asm_disk order …...
掌握Rust模式匹配:从基础语法到实际应用
本篇文章将探讨 Rust 编程语言中至关重要的特性之一——模式匹配。Rust 语言的模式匹配功能强大,不仅能处理简单的值匹配,还能解构和操作复杂的数据结构。通过深入学习模式匹配,程序员可以更加高效地编写出清晰、简洁且易于维护的代码。 Rus…...
HFSS 3D Layout中Design setting各个选项的解释
从HFSS 3D LAYOUT菜单中,选择Design Settings打开窗口,会有六个选项:DC Extrapolation, Nexxim Options, Export S Parameters, Lossy Dielectrics, HFSS Meshing Method, and HFSS Adaptive Mesh. DC Extrapolation 直流外推 直流外推分为标…...
线性表之链表详解
欢迎来到我的:世界 希望作者的文章对你有所帮助,有不足的地方还请指正,大家一起学习交流 ! 目录 前言线性表的概述链表的概述 内容链表的结构链表节点的定义 链表的基本功能单向链表的初始化链表的插入操作头插操作尾插操作 链表的删除操作头…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...
【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...
使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...
JavaScript 数据类型详解
JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型(Primitive) 和 对象类型(Object) 两大类,共 8 种(ES11): 一、原始类型(7种) 1. undefined 定…...
