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

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

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

相关文章

项目更换服务器时间少8小时

时区错误 输入 date 查看当前的linux系统时间 hwclock --show 查看当前linux硬件时间 如果发现系统时间和硬件时间不同步&#xff0c;而且硬件时间是正确的&#xff0c;可以用以下命令&#xff1a;hwclock --hctosys 把硬件时间同步到系统时间 mysql时区错误可以参考这位大…

云服务器重启后无法访问phpMyAdmin的解决方案

一、背景 我在解决另一问题的时候&#xff0c;重启了云服务器。当我想再次访问phpMyAdmin时&#xff0c;出现了如下报错。 HTTPConnectionPool(host127.0.0.1, port888): Max retries exceeded with url: /phpmyadmin_609a02b02423214c/index.php?langzh_cn (Caused by Ne…

嵌入式知识学习分享~~

欢迎关注小徐的个人微信公众号&#xff0c;感觉微信的文章排版不错&#xff0c;后续会继续分享一些新的学习记录以及和大家共同进步~

基于广义极大极小凹惩罚的心电信号降噪方法(MATLAB R2021B)

凸优化是数学最优化的一个子领域&#xff0c;研究定义于凸集中的凸函数最小化问题。由于心电信号降噪的过程可以理解为求信号的稀疏近似解&#xff0c;因此基于凸优化和稀疏性表达的去噪方法可用于心电信号处理。在凸优化的数学模型中&#xff0c;惩罚项的选取对最终结果会产生…

如何在测试/线上环境页面访问本地接口?

文章目录 一、前言二、分析三、搭建1、搭建nginx&#xff0c;监听http请求转发2、监听https请求转发 四、总结 一、前言 在工作中&#xff0c;开发完的接口&#xff0c;一般测试的话&#xff0c;基本是使用Postman&#xff0c;如果要到页面测试&#xff0c;就要发版进行测试&a…

年中汇报季?——一文教会你如何进行数据分析

一、常见的数据分析报告类型 数据分析报告通常可以分为三类&#xff1a;日常分析报告、专题型分析报告和综合性分析报告。前两者是以数据结论建议的格式去撰写&#xff0c;综合性分析报告则是&#xff1a;行业环境调研&#xff08;竞品类产品数据分析&#xff09;自身产品数据…

Java:使用Opencv进行大图找小图

本文将介绍使用Java操作Opencv进行行大图找小图测试&#xff01; 1、OpenCV简介 OpenCV是一个开源计算机视觉库&#xff0c;提供了丰富的图像处理和计算机规觉算法。它支持多种编程语言&#xff0c;包括Java本文将介绍如何在Java中使用OpenCV进行图像处理和计算机视觉任务。 …

自然语言处理(NLP)—— 信息提取与文档分类

1. 初识信息提取 1.1 信息提取的基本知识 1.1.1 信息提取的概念 信息提取&#xff08;IE, Information Extraction&#xff09;是自然语言处理&#xff08;NLP&#xff09;领域的一个重要分支&#xff0c;它专注于从文档或语料库中提取结构化信息。这与信息检索&#xff08;I…

RabbitMQ怎么保证可靠性

RabbitMQ怎么保证可靠性 前言生产端问题解决方案代码验证 RabbitMQ问题消费端问题解决方案代码验证 总结 前言 RabbitMQ相信大家都非常熟悉了&#xff0c;今天咱们来聊聊怎么保证RabbitMQ的可靠性。 那什么时候会出现问题呢&#xff1f; 第一种是生产端出现的问题。我们向队…

MySQL-事务日志

事务的隔离性由 锁机制 实现 事务的原子性、一致性、隔离性 由事务的 redo日志 和 undo 日志来保证 redo log 称为 重做日志&#xff0c;提供再写入操作&#xff0c;恢复提交事务修改的页操作&#xff0c;用来保证事务的持久性。undo log 称为 回滚日志&#xff0c;回滚行记录…

selenium自动化介绍

文章目录 一、selenium原理 安装二、selenium使用1.创建浏览器对象&#xff0c;访问网址2.消除警告提示3.不显示浏览器中受控制字样4.防检测5.设置延时5.1强制延时5.2隐式延时 6.设置浏览器窗口大小 三、案例实战&#xff1a;百度搜索四、iframe标签五、案例实战&#xff1a;Q…

第一周 数据结构与算法以及复杂度分析

数据结构与算法 算法定义 算法&#xff08;algorithm&#xff09;是在有限时间内解决特定问题的一组指令或操作步骤&#xff0c;它具有以下特性。 1.问题是明确的&#xff0c;包含清晰的输入和输出定义。 2.具有可行性&#xff0c;能够在有限步骤、时间和内存空间下完成。 3.…

【第五节】C++的多态性与虚函数

目录 前言 一、子类型 二、静态联编和动态联编 三、虚函数 四、纯虚函数和抽象类 五、虚析构函数 六、重载&#xff0c;重定义与重写的异同 前言 面向对象程序设计语言的三大核心特性是封装性、继承性和多态性。封装性奠定了基础&#xff0c;继承性是实现代码重用和扩展…

Linux内网中安装jdk1.8详细教程

本章教程,主要介绍如何在内网环境中配置JDK1.8环境变量 一、下载Linux版压缩包 下载地址:https://www.oracle.com/java/technologies/downloads/#java8 下载完成之后,通过XFTP等工具,将安装包上传到内网服务器 二、安装配置步骤 1、解压压缩包 tar -zxvf /usr/local/jdk-…

根据PDF模版填充数据并生成新的PDF

准备模版 使用 福昕高级PDF编辑器 &#xff08;本人用的这个&#xff0c;其他的也行&#xff0c;能作模版就行&#xff09;打开PDF文件点击 表单 选项&#xff0c;点击 文本域在需要填充数据的位置设计文本域设置 名称、提示名称相当于 属性名&#xff0c;提示就是提示&#x…

基于SSM的“基于Apriori算法的网络书城”的设计与实现(源码+数据库+文档)

基于SSM的“基于Apriori算法的网络书城”的设计与实现&#xff08;源码数据库文档) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SSM 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 网站功能展示图 首页 商品分类 热销 新品 我的订单 个…

二位偏序,P3660 [USACO17FEB] Why Did the Cow Cross the Road III G

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 P3660 [USACO17FEB] Why Did the Cow Cross the Road III G - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 二、解题报告 1、思路分析 二维偏序问题 我们将坐标按照第一维排序 然后树状数组维护区间内的…

【深度学习】【STWave】时空图预测,车流量预测,Efficient Spectral Graph Attention Network

Spatio-Temporal meets Wavelet: Disentangled Traffic Flow Forecasting via Efficient Spectral Graph Attention Network 代码&#xff1a;https://github.com/LMissher/STWave 论文&#xff1a;https://arxiv.org/abs/2112.02740 帮助&#xff1a; https://docs.qq.com/s…

C++STL---vector模拟实现

通过上篇文章&#xff0c;我们知道vector的接口实际上和string是差不多的&#xff0c;但是他俩的内部结构却大不一样&#xff0c;vector内有三个成员变量&#xff1a;_start、_finish、_endofstorage: _start指向容器的头元素&#xff0c;_finish指向有效元素末尾的元素&#x…

Vue2 + Element UI 封装 Table 递归多层级列表头动态

1、在 components 中创建 HeaderTable 文件夹&#xff0c;在创建 ColumnItem.vue 和 index.vue。 如下&#xff1a; 2、index.vue 代码内容&#xff0c;如下&#xff1a; <template><div><el-table:data"dataTableData"style"width: 100%"…