c++ string模拟实现

模拟实现string类,里面包含四个成员变量,第一个是指向字符数组的指针,第二个变量是目前存放了多少个字符,第三个变量为这个字符数组的容量的大小。最后一个为静态成员变量npos。

注意:一个const 修饰的整型,静态成员变量可以在类里面初始化(三者缺一不可),而其他成员变量,要声明定义分离,类中声明,类外定义。
    char* _str;
    size_t _size;
    size_t _capacity;
    const static size_t npos=-1;
 

1,构造函数

可以写一个缺省参数,在实例化对象时没有进行初始化时,可以初始化为缺省参数,缺省参数为""空。另一个需要注意的为,capacity是显示的是实际容量,所以在申请内存的时候有多申请一个来放'\0'。

string(const char* str="")
    :_size(strlen(str))
    ,_capacity(_size)
{
    _str = new char[_capacity + 1];
    strcpy(_str, str);
}

2,析构函数

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

3,拷贝构造

两种方法:

常规:申请一块新的空间,然后将参数字符数组拷贝到这块新空间里。注意:这里也需要申请空间,所以也需要注意申请空间的大小

string(const string& s) {
            _str = new char[s._capacity+1];
            strcpy(_str, s._str);
            _size = s._size;
            _capacity = s._capacity;   }

优化:实例化一个临时对象。然后用参数的字符数组初始化这个临时对象。然后将这里临时对象和*this进行交换。而交换后的临时对象出了作用域将会自动被销毁。这里需要注意,这个临时对象是没有初始化的,有些编译器可能会出问题,保险起见,我们应该将*this初始化,这样两者交换后,已初始化的临时对象销毁将没有问题。

void swap(string& s) {
    std::swap(s._str, _str);
    std::swap(s._size, _size);
    std::swap(s._capacity, _capacity);
}

string(const string& s) 
    :_str(nullptr)
    ,_size(0)
    ,_capacity(0)
{
    string tmp(s._str);
    swap(tmp);
}

4,迭代器

首先定义两种迭代器

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;}

5,reserve()

对存放字符的空间进行扩容(扩大_capacity),首先判断,所需要的空间是否大于原有的空间,如果符合要求,则申请一块新的空间(空间大小为n+1),然后将原有的数据拷贝到新的空间中,然后将原本的空间清空,_str指向这个块新空间的地址。

void reserve(size_t n) {
    if (n > _capacity) {
        _capacity = n;
        char* tmp = new char[n+1];
        strcpy(tmp, _str);//把"/0"也拷贝过去
        delete[]_str;
        _str = tmp;
    }
}

6,push_back()

只需要注意,最后在_size的位置加 '\0'

void push_back(char ch) {
    if (_size == _capacity) {
        reserve(_capacity==0 ? 4 :_capacity * 2);
    }
    _str[_size] = ch;
    _size++;
    _str[_size] = '\0';
}

7,capacity(),size()

size_t capacity()const {
    return _capacity;
}

size_t size() const{
    return _size;
}

8,append()

拼接,首先要判断拼接后的的大小是否开辟的空间会放不下,如果放不下调用reserve,如果放得下在_str + _size插入str

void append(const char* str) {
    size_t len = strlen(str);
    if (_size +len>_capacity) {
        reserve(_size + len);
    }
    strcpy(_str + _size, str);
    _size += len;
}

9,insert()

分为插入字符和插入字符串

先判断下标的合理性,然后判断是否有空间加入这个字符。然后从字符串的最后 '\0'开始一次向后走一个,直到走到要插入的下标为止,这时将要插入的字符插到该下标

void 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++;
}

插入字符串与之相类似,只是'\0'向后走要插入的字符串的长度,注意这里不可以用strcpy,因为这个函数会将字符串的'\0'也复制过去,所以应该用函数strncpy,从而指定要复制的长度

void insert(size_t pos, const char* str) {
    assert(pos <= _size);
    size_t len = strlen(str);
    if (_capacity < len + _size) {
        reserve(_size + len);
    }
    size_t end = _size+1;
    while (end>pos)
    {
        _str[end+len] = _str[end - 1];
        end--;
    }
    strncpy(_str + pos,str,len);
    _size +=  len;

}

10,erase()

从下标为pos开始删,删除len个元素,首先判断要开始删的地方加上要删的个数是否超过了_size的长度,如果超过了,则说明从这个下标开始后面的元素都要删。那么就可以直接将该下标的值改为'\0',如果没有超过,那么得到要删除的最后一个元素的下一个,然后将这个元素向前移len长个,后面的元素以此类推向前推移。

void erase(size_t pos,size_t len =npos) {
    assert(pos < _size);
    if (len == npos || pos + len >= _size) {
        _str[pos] = '\0';
        _size = pos;
    }
    else {
        size_t begin = pos + len;
        while (begin<=_size)
        {
            _str[begin-len] = _str[begin];
            begin++;
        }
        _size -= len;
    }
}

11,resize()

要将size长度变为n,如果当前的长度大于n那么要删除多出来的元素,如果小于n,要在没有数据的空间中添加'\0'。

首先判断,size是大于n还是小于n。如果大于n,那么就要删除,可以在n下标的地方直接添加'\0'。如果小于n,要先判断空间是否有n这么大,如果没有要先扩容,然后在当前_size位置即以后位置加入'\0',直到下标为n为止

void resize(size_t n, char ch = '\0') {
    if (n<=_size)
    {
        _str[n] = '\0';
        _size = n;
    }
    else {
        reserve(n);
        while (_size<n) {
            _str[_size] = ch;
            _size++;
        } 
        _str[_size] = '\0';
    }
}

12,find()

寻找字符或者,字符串

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* sub, size_t pos = 0) {
    const char* p = strstr(_str+pos, sub);
    if (p)
    {
        return p - _str;
    }
    else {
        return npos;
    }    
}

13,substr()

首先判断要开始截取的地方加上要截取的长度是否超过了_size的长度,如果超过了,则说明从这个下标开始后面的元素都要被截取。实例化一个对象,将截取的部分+到这个对象的空间里面,然后返回这个对象

string substr(size_t pos, size_t len = npos) {
    string s;
    size_t end = pos + len;
    if (len==npos||end>=_size){
        len = _size - len;
        end = _size;
    }
    s.reserve(len);
    for (size_t i = pos; i < end; i++)
    {
        s += _str[i];
    }
    return s;//拷贝构造+赋值
}

14,运算符重载

(1)operator[ ],

需要提供两种,一种能读不能写,一种能读也能写

char& operator[](size_t pos) {
    assert(pos < _size);
    return _str[pos];
}

const char& operator[](size_t pos) const {
    assert(pos < _size);
    return _str[pos];
}

(2)operator=

常规:我们需要申请一块与参数中字符数组一样大的临时空间。然后将字符数组中的值拷贝到这块临时空间中,清空*this的空间,然后将临时空间的地址赋值给this所指空间的地址。然后将其他变量意义拷贝一下

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;
        }

优化:

我们可以实例化一个临时对象,然后利用拷贝构造初始化这个临时对象。然后将临时对象与*this进行交换。

string& operator=(const string& s) {
    if (*this != s)
    {
        string tmp(s);
        swap(tmp);//赋值时,把原本的空间释放了
    }
    return *this;
}

优化:再传参的时候就实例化一个临时对象

string& operator=(string tmp ) {
    swap(tmp);//赋值时,把原本的空间释放了
    return *this;
}

(3)operator+=

重载一个+=字符的,重载一个+=字符串的

string& operator+=(char ch) {
    push_back(ch);
    return *this;
}

string& operator+=(char* str) {
    append(str);
    return *this;
}

(4)<,==,<=,>,>=,!=

bool operator<( const string& s)const {
    return strcmp(_str, s._str) < 0;
}
bool operator==(const string& s) const {
    return strcmp(_str, s._str) == 0;
}
bool operator<=(const string& s) const {
    return *this == s || *this < s;
}
bool operator>(const string& s)const {
    return !(*this <= s);
}
bool operator>=(const string& s) const {
    return !(*this < s);
}
bool operator!=(const string& s)const {
    return !(*this == s);
}

(5)operator<<

注意:这并不是类的成员函数。

之前已经将迭代器实现了,所以可以使用范围for来进行遍历。

ostream& operator<<(ostream& out , const string& s) {
    for (auto ch : s) {
        cout << ch << " ";
    }//因为s是const修饰的,所以要写const相关的函数
    return out;
}

(6)operator>>

注意:这并不是类的成员函数。

常规:

用get函数去捕获字符,然后+=到引用上,直到遇到“ ”或者“\0”停止。

istream& operator>>(istream& in,  string& s) {
    char ch;
    s.clear();
    ch = cin.get();
    while (ch != ' ' && ch != '\n ')
    {
        s += ch;
        ch = cin.get();
    }
    return in;
}

优化:建立一个固定大小的数组,将输入的字符先存放到数组中,然后统一将数组中的字符+=到引用中,这样可以防止内存碎片化

istream& operator>>(istream& in, string& s) {
    s.clear();
    char ch;
    ch = in.get();
    char buff [129];
    int i = 0;
    while (ch != ' ' && ch != '\n ')
    {
        buff[i++] = ch;
        if (i==128) {
            buff[i] = '\0';
            s += buff;
            i = 0;
        }
        ch = in.get();
    }
    if (i != 0) {
        buff[i] = '\0';
        s += buff;
    }
    return in;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vs2019 无法打开QT的UI文件

/* * --------------------------- Microsoft Visual StudioQt5.15.2\5.15.2\msvc2019_64 --------------------------- D:\QT_Project_vs\QtWidgetsApplication1\QtWidgetsApplication1\QtWidgetsApplication1.ui 无法打开文件。 --------------------------- 确定 -------…

人工智能学习笔记(2):认识和安装Stable Diffusion

人工智能学习笔记&#xff08;2&#xff09;&#xff1a;认识和安装Stable Diffusion 文章目录 人工智能学习笔记&#xff08;2&#xff09;&#xff1a;认识和安装Stable DiffusionStable Diffusion的起源和发展历程Stable Diffusion的应用场景基本原理文本到图像的转换过程潜…

全网唯一:触摸精灵iOS版纯离线本地文字识别插件

目的 触摸精灵iOS是一款可以模拟鼠标和键盘操作的自动化工具。它可以帮助用户自动完成一些重复的、繁琐的任务&#xff0c;节省大量人工操作的时间。但触摸精灵的图色功能比较单一&#xff0c;无法识别屏幕上的图像&#xff0c;根据图像的变化自动执行相应的操作。本篇文章主要…

vs2019 QT UI 添加新成员或者控件代码不提示问题解决方法

右键点击头文件&#xff0c;添加ui的头文件 添加现有项 找到uic目录的头文件 打开ui,QtWidgetsApplication2.ui,进行测试 修改一个名字&#xff1a; 重点&#xff1a; 设置一个布局&#xff1a; 点击生成解决方案&#xff1a; 以后每次添加控件后&#xff0c;记得点击保存 这样…

Matlab|基于粒子群算法优化Kmeans聚类的居民用电行为分析

目录 主要内容 部分代码 结果一览 下载链接 主要内容 在我们研究电力系统优化调度模型的过程中&#xff0c;由于每天负荷和分布式电源出力随机性和不确定性&#xff0c;可能会优化出很多的结果&#xff0c;但是经济调度模型试图做到通用策略&#xff0c;同样的策…

hot100 -- 回溯(下)

&#x1f442; ​​​​​​​▶ 幸福就是 (163.com) &#x1f442; ▶ 当爱在靠近 (163.com) 目录 &#x1f6a9;括号生成 AC DFS &#x1f33c;单词搜索 AC DFS &#x1f382;分割回文串 AC DFSDP AC DFS记忆化 &#x1f33c;N 皇后 AC DFS &#x1f6a9;括号…

TS38.300中的切换流程(很一般)

本文根据3GPP R18 TS 38.300第9.2.3节整理 切换(Handover)是移动终端(UE)进入RRC_CONNECTED状态后在不同服务小区(Cell)之间保持与网络联系唯一手段&#xff0c;期间首先通过控制面(C-Plane)进行无线测量、切换协商及触发等&#xff1b;为此3GPP在TS38.300中定义如下。 RAN系统…

Github 2024-05-31 Java开源项目日报 Top10

根据Github Trendings的统计,今日(2024-05-31统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Java项目10TypeScript项目1JavaGuide - Java 程序员学习和面试指南 创建周期:2118 天开发语言:Java协议类型:Apache License 2.0Star数量:1…

IDEA 打开项目后看不到项目结构怎么办?

1、先把这个项目从 IDEA 中移除 2、再重新打开或导入 3、如果还没有解决&#xff0c;就先把这个项目拷贝出来把原来的路径上的项目给删除&#xff0c;然后再把拷贝后的项目放在一个路径下&#xff0c;再打开就可以了

C# 流程图demo

1、向panel添加控件。 2、panel控件中的控件可以自由拖动。 3、控件之间连线。 4、连线的控件&#xff0c;拖动时更新连线。 流程图连接线 //流程图连接线private void draggablePanel1_Paint(){Graphics g this.draggablePanel1.CreateGraphics();g.Clear(this.BackColor…

使用Python操作Git

大家好&#xff0c;当谈及版本控制系统时&#xff0c;Git是最为广泛使用的一种&#xff0c;而Python作为一门多用途的编程语言&#xff0c;在处理Git仓库时也展现了其强大的能力。通过Python&#xff0c;我们可以轻松地与Git仓库进行交互&#xff0c;执行各种操作&#xff0c;从…

leetcode148. 排序链表,归并法,分治的集大成之作

leetcode148. 排序链表 题目链接 给你链表的头结点 head &#xff0c;请将其按升序排列并返回排序后的链表。 示例 1&#xff1a; 输入&#xff1a;head [4,2,1,3] 输出&#xff1a;[1,2,3,4] 输入&#xff1a;head [-1,5,3,4,0] 输出&#xff1a;[-1,0,3,4,5] 示例 3&…

STM32 | 超声波实战

​01、上节回顾 STM32 | HC-SR04 超声波测距模块 | DHT11数字温湿度传感器(第七天)STM32 | 数字温湿度传感器DHT11STM32 | HC-SR04 超声波测距模块STM32 | DHT11数字温湿度传感器实战02、超声波图示 03、超声波头文件 #ifndef __SR04_H#define __SR04_H​#include "stm…

HNU-深度学习-电商多模态图文检索

前言 主要是跟着baseline搭了一遍&#xff0c;没有想到很好的优化。 有官方教程&#xff0c;但是有点谬误&#xff0c;所以就想着自己记录一下我的完成过程。 github项目地址&#xff1a; https://github.com/OFA-Sys/Chinese-CLIP 官方文档&#xff1a; 电商多模态图文检…

Django中使用Celery和APScheduler实现定时任务

在之前的文章我们已经学习了Celery和APScheduler的基本使用&#xff0c;下面让我们来了解一下如何在Django中使用Celery和APScheduler Celery 1.前提工作 python 3.7 pip install celery pip install eventlet #5.0版本以下 pip install importlib-metadata4.8.3&#xff08…

Git系列:rev-parse 使用技巧

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

【机器学习300问】106、Inception网络结构如何设计的?这么设计的目的是什么?

谷歌的Inception网络&#xff0c;也被称为GoogLeNet&#xff0c;是Google在2014年推出的一种深度卷积神经网络&#xff08;CNN&#xff09;模型&#xff0c;在这之前的AlexNet、VGG等结构都是通过增大网络的深度&#xff08;层数&#xff09;来获得更好的训练效果&#xff0c;但…

车载监控解决方案在工程机械行业的应用

随着科技的快速发展&#xff0c;现代工程机械行业正迎来一场智能化、信息化的革命。GPS、4G通信、车载监控以及车载智能应用等技术的综合运用&#xff0c;为工程机械的安全作业提供了全方位、全时段的保障。本文以挖掘机为例&#xff0c;探讨车载监控解决方案在工程机械行业的广…

cleanmyMac有必要吗,什么软件可以替代clean my mac

最近总有苹果用户抱怨mac电脑变得非常卡顿&#xff0c;而且总会收到“您的启动磁盘几乎已经满了”的系统提示。提示出现的原因是我们长期未对电脑进行健康扫描和深度清理导致的。遇到这种情况&#xff0c;我们可以借助专业的电脑深度清理软件——CleanMyMac X&#xff0c;清理不…

漫画:什么是通用人工智能?

窄人工智能&#xff0c;对应英文Artificial Narrow Intelligence&#xff0c;简称ANI&#xff0c;也被称为特定任务人工智能。 顾名思义&#xff0c;窄人工智能用于完成某一项或几项特定的任务&#xff0c;比如智能驾驶、人脸识别、AlphaGo、AI绘画、大语言模型等等&#xff0c…