【c++】string类的模拟实现

目录

介绍:

一,构造函数和析构函数

二,赋值运算符与流运算符

三,迭代器和运算符重载

四,容器接口函数的实现

1,增删操作

2,查找与插入

3,接口的常规操作


介绍:

        STL容器的模拟实现是我们了解STL函数接口的重要部分,也是在学习C++容器时的重要环节。这里,我们模拟实现string的常规接口。

steing的框架结构:

        在调试窗口上可观察到string的参数有size(大小)、capacity(大小)、串(char*)。至于迭代器,这里可先认为指针。

//框架设计模型

class string
{
public:
    typedef char* iterator;   //迭代器
private:
    char* _str;               //串
    size_t _capacity;    //容量
    size_t _size;           //大小
};


一,构造函数和析构函数

        构造函数在实现时首先要注意缺省值的情况。当创建string空类时,默认不会初始化,且输出时不会输出乱码,无任何数据,因此,这里需将缺省值设为""

//String(const char* str = "\0") 错误示范,可能会出现程序崩溃
//String(const char* str = nullptr) 错误示范,可能会出现程序崩溃

//普通构造
string(const char* str = "")
     : _capacity(strlen(str) + 1)
     , _size(strlen(str))
{
     // 构造String类对象时,如果传递nullptr指针,可以认为程序非
     if (nullptr == str)
     {
         assert(false);
         return;
     }
     _str = new char[_capacity];
     memcpy(_str, str, _capacity);
}
//拷贝构造
string(const string& s)
    : _capacity(s._capacity)
    , _size(s._size)
{

    // 构造String类对象时,如果传递nullptr指针,可以认为程序非
    if (nullptr == s)
    {
        assert(false);
        return;
    }
    _str = new char[_capacity];
    memcpy(_str, s._str, s._size + 1);
}

//析构函数

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

}


二,赋值运算符与流运算符

        赋值运算符重载跟构造函数的注意事项一样,需注意的是这里进行的是深拷贝,不是浅拷贝,即重新开辟新的空间进行拷贝。

string& operator=(const string& s)
{
    delete[] _str;
    char* str = new char[s._capacity];
    memcpy(str, s._str, s._size + 1);
    _str = str;
    _size = s._size;
    _capacity = s._capacity;
    return *this;
}

        流的操作符中,输出流实现简单,只需根据string类的大小进行输出字符即可,但输入流需注意以下两点:

  1. 在进行对string输入时,若里面有数据,需将其清空。
  2. string类的输入操作可能会影响容量的变化和大小的变化,若容量不够需进行扩容。

//输出流

ostream& operator<<(ostream& _cout, const bit::string& s)
{
    for (int i = 0; i < s._size; i++)
    {
        _cout << s._str[i];
    }
    return _cout;
}

//输入流

istream& operator>>(istream& _cin, bit::string& s)
{
    //先清理数据string中的数据
    delete[] s._str;
    char* str = new char[s._capacity];
    s._str = str;
    memcpy(s._str, "\0", 1);
    s._size = 0;
    //下面进行开始输入操作
    char buff[128];
    char ch = _cin.get();
    int i = 0;
    while (ch != ' ' && ch != '\n')
    {
        buff[i++] = ch;
        if (i == 127)
        {
            buff[i] = '\0';
            //在进行增添之前要先判断容量是否够容载
            if (s._capacity < s._size + strlen(buff))
            {
                s._capacity += 2 * (s._size + strlen(buff));
                char* str = new char[s._capacity];
                memcpy(str, s._str, s._size + 1);
                delete[] s._str;
                s._str = str;
            }
            //连接操作,注意:这里不能用strcat,因为strcat本身有bug,如下

           /*char str[] = "\0";
            strcat(str, "avbsc");
            cout << str << endl;
            此程序将会崩溃,strcat的第一个参数不能为空*/

            int k = 0;
            for (int j = s._size; j < s._size + strlen(buff); j++)
            {
                s._str[j] = buff[k++];
            }
            s._size += strlen(buff);
            i = 0;
        }
        ch = _cin.get();
    }
    buff[i] = '\0';
    //判断容量
    if (s._capacity < s._size + strlen(buff))
    {
        s._capacity += 2 * (s._size + strlen(buff));
        char* str = new char[s._capacity];
        memcpy(str, s._str, s._size + 1);
        delete[] s._str;
        s._str = str;
    }
    //连接操作
    int k = 0;
    for (int j = s._size; j < s._size + strlen(buff); j++)
    {
        s._str[j] = buff[k++];
    }
    s._size += strlen(buff);
    return _cin;
}


三,迭代器和运算符重载

        我们目前最常用的迭代器是begin()和end(),begin()指向首元素的地址,而end()指向尾元素的下一个地址处,实现机制如下:

iterator begin()
{
    return _str;
}
iterator end()
{
    return _str + _size;
}

        至于运算符重载的实现,这里,我们对 “[]、>、>=、<、<=、==、!=” 进行重载实现。

char& operator[](size_t index)
{
    //防止遍历出界
    assert(index <= _size && index >= 0);
    return *(_str + index);
}
const char& operator[](size_t index)const
{
    //防止遍历出界
    assert(index <= _size && index >= 0);
    return (const char)(*_str + index);
}
bool operator<(const string& s)
{
    for (int i = 0; i < _size; i++)
    {
        if (_str[i] < s._str[i])
        {
            return true;
        }
        else if (_str[i] > s._str[i])
        {
            return false;
        }
    }
    return false;
}
bool operator<=(const string& s)
{
    for (int i = 0; i < _size; i++)
    {
        if (_str[i] < s._str[i])
        {
            return true;
        }
        else if (_str[i] > s._str[i])
        {
            return false;
        }
    }
    return true;
}
bool operator>(const string& s)
{
    return !(*this <= s);
}
bool operator>=(const string& s)
{
    return !(*this < s);
}
bool operator==(const string& s)
{
    return !(*this > s) && !(*this < s);
}
bool operator!=(const string& s)
{
    return !(*this == s);
}


四,容器接口函数的实现

1,增删操作

        在实现增删之前,我们首先实现reserve增容接口。需注意reserve不能减小容量,只能增加容量。

void reserve(size_t n)
{
    if (_capacity < n)   //不能减小容量
    {
        _capacity = _capacity == 1 ? n : 2 * n;
        char* str = new char[_capacity];
        memcpy(str, _str, strlen(_str) + 1);
        delete[] _str;
        _str = str;
    }
}

        这里我们对push_back、erase、+=、append操作进行接口实现。其中,push_back只能在末尾增添一个字符,erase可删除多个字符或全部字符,“+=” 和append都是在末尾连接一个串。在实现中,要注意string为空和满载的情况。当满载时,需使用reserve进行增容操作,当为空时,要根据接口的功能来进行下一步作用。

//删除操作

string& erase(size_t pos, size_t len)
{

    //防止错误操作
    assert(len >= 0);
    assert(pos >= 0 && pos < _size);
    int deletelen = len;
    if (deletelen > _size - pos) //注意当len大于string大小的情况
    {
        deletelen = _size - pos;
    }
    int _strlen = _size + 1;
    memcpy(_str + pos, _str + pos + deletelen, _strlen - pos - deletelen);
    return *this;
}

//增加操作

void push_back(char c)
{
    if (_size == _capacity - 1)
    {
        reserve(_capacity + 1);
    }
    _str[_size++] = c;
    _str[_size] = '\0';
}

void append(const char* str)
{
    if (_capacity - 1 < _size + strlen(str))
    {
        reserve(_size + strlen(str));
    }
    for (int i = 0; i < strlen(str); i++)
    {
        push_back(str[i]);
    }
}

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

2,查找与插入

        这里我们模拟find查找和insert插入。实现find查找,若找到指定的数据,返回第一次出现的位置,若没有找到,返回无符号整型-1的数值。实现insert插入,只需注意要插入的位置在合理位置上即可。

//find查找操作
size_t find(char c, size_t pos = 0) const   //返回c在string中第一次出现的位置
{
    for (int i = pos; i < _size && pos >= 0; i++)
    {
        if (_str[i] == c)
        {
            return i;
        }
    }
    return -1;
}
size_t find(const char* s, size_t pos = 0) const  //返回子串s在string中第一次出现的位置
{
    for (int i = pos; i < _size && pos >= 0; i++)
    {
        int k = i;
        for (int j = 0; j < strlen(s) && i < _size; j++)
        {
            if (s[j] == _str[i])
            {
                if (i - k + 1 == strlen(s))
                    return k;
                i++;
            }
            else
                break;
        }
        i = k;
    }
    return -1;
}

//insert插入操作
string& insert(size_t pos, char c)
{
    assert(pos >= 0 && pos <= _size);   //保证插入的位置在合理范围中
    if (_capacity == _size)
    {
        reserve(_capacity + 1);
    }
    for (int i = _size; i >= pos; i--)
    {
        _str[i + 1] = _str[i];
    }
    _str[pos] = c;
    _size++;
    return *this;
}
string& insert(size_t pos, const char* str)
{
    assert(pos >= 0 && pos <= _size);    //保证插入的位置在合理范围中
    if (_capacity < _size + strlen(str) + 1)
    {
        reserve(_size + strlen(str) + 2);
    }
    for (int i = _size; i >= pos; i--)
    {
        _str[i + strlen(str)] = _str[i];
    }
    int j = 0;
    for (int i = pos; i < pos + strlen(str); i++)
    {
        _str[i] = str[j++];
    }
    _size += strlen(str);
    return *this;
}

3,接口的常规操作

        string容器其它的常规接口有resize、swap、c_str、size、capacity、empty、clear七大函数操作。之前已经说明了这七个接口的功能,它们的实现原理也很简单,这里就不做说明,代码如下:

void swap(string& s)
{
    string tem = s;
    s = *this;
    *this = tem;
}
const char* c_str()const
{
    return _str;
}
size_t size()const
{
    return _size;
}
size_t capacity()const   
{
    return _capacity;
}
bool empty()const
{
    return _size == 0 ? true : false;
}

void resize(size_t n, char c = '\0')
{
    assert(n >= 0);
    for (int i = _size; i < n; i++)
    {
        push_back(c);
    }
    _size = n;
    _str[_size] = '\0';
}
void clear()    //注意:clear不会清理空间,只是将数据清理掉
{
    delete[] _str;
    char* str = new char[_capacity];
    _str = str;
    memcpy(_str, "", 1);
    _size = 0;
}

        以上是对string容器实现的常规接口操作,实现的功能与正规的功能具有差别,这里只需保证我们实现功能用法与正规的一样并保证操作时不会出现bug即可。特别注意的是构造与析构、赋值运算符的模拟实现,这种形式在公司面试和笔试上经常会出现。

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

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

相关文章

STM32 map文件详解

文章目录 1. 前言2. 生成 .map 文件3 .map 文件的组成3.1 Section Cross References - 各个源文件之间函数的调用关系3.2 Removing Unused input sections from the image - 移除未使用的模块3.3 Image Symbol Table - 映射符号表&#xff1a;描述各&#xff08;程序段 / 数据&…

Spark分布式内存计算框架

目录 一、Spark简介 &#xff08;一&#xff09;定义 &#xff08;二&#xff09;Spark和MapReduce区别 &#xff08;三&#xff09;Spark历史 &#xff08;四&#xff09;Spark特点 二、Spark生态系统 三、Spark运行架构 &#xff08;一&#xff09;基本概念 &#x…

Antd v5.8 modal.confirm 手动控制关闭

开发中经常需要使用操作提示弹窗&#xff0c;在 Antd 中的 Modal 组件提供了 confirm 方法&#xff0c;可以快速实现操作提示弹窗。本文就来介绍一下如何使用 Modal.useModal 创建弹窗&#xff0c;并且实现手动控制弹窗的关闭逻辑。 一、代码演示 import { message, Modal } f…

C++ SEH结构化异常捕获处理(双平台支持 Linux、Windows)。

测试&#xff1a; try_ctor();try_call([](){printf("1111111111111111111111\r\n");int* p NULL;*p 100;throw 1;// try_eeh();}, [](){printf("2222222222222222222222\r\n");}); 设置NULL指针P的值引发程式崩溃&#xff0c;可以被正确捕获&#xff0…

禾匠榜店商城系统 RCE漏洞复现

0x01 产品简介 禾匠榜店商城系统是浙江禾匠信息科技有限公司的一套基于PHP和MySQL的商城系统。 0x02 漏洞概述 禾匠榜店商城系统的api/testOrderSubmit模块下的preview方法存在命令执行漏洞,攻击者可以向服务器写入木马文件,直接获取服务器权限 0x03 漏洞概述 FOFA:bod…

2312llvm,编译X86的clang与llvm

先从这里下载llvm-project. 然后解压后,进入clang目录. 输入命令: cmake -DLLVM_ENABLE_PROJECTSclang -G "Visual Studio 17 2022" -A Win32 -Thostx64 ..\llvm表示,用x64的cl编译X86版本,用-A Win32代表X86版本. 然后生成llvm.sln有2M大. 编译选项技巧: 因为,内存…

带你手把手解读firejail沙盒源码(0.9.72版本) (三) fcopy

文章目录 main.c该模块的各个函数功能详解selinux_relabel_pathcopy_filemkdir_attrcopy_linkproc_pid_to_selffs_copydircheckduplicate_dirduplicate_fileduplicate_linkmain Makefile main.c 文件总结 ├── fcopy │ ├── Makefile │ └── main.cmain.c #include…

【腾讯云HAI域探密】- HAI为NPL保驾护航

近些年&#xff0c;随着机器学习技术的蓬勃发展&#xff0c;以GPU为代表的一系列专用芯片以优越的高性能计算能力和愈发低廉的成本&#xff0c;在机器学习领域得到广泛认可和青睐。GPU等专用芯片以较低的成本提供海量算力&#xff0c;已经成为机器学习和AI人工智能领域的核心利…

用EXCEL计算NTC、BS、电压等AD参数

前言 之前计算NTC的AD值算得很麻烦&#xff0c;因为51内核的单片机不支持除法运算&#xff0c;更别说浮点运算了。 EXCEL自动算出参数就显得很方便了。 有纰漏请指出&#xff0c;转载请说明。 学习交流请发邮件 1280253714qq.com 理论基础 参考这篇文章NTC热敏电阻温度采集…

企业数字化转型进入深海区:生成式AI时代下如何制定数据战略

云计算适用于任何人&#xff0c;任何企业&#xff0c;云计算的分支——人工智能(AI)正发展的迅疾如火&#xff0c;炙手可热。特别是ChatGPT已经挑战各行各业对于AI的认知。 作为全球云计算领域的年度风向标活动&#xff0c;12月12日&#xff0c;亚马逊云科技2023 re:Invent中国…

Web漏洞分析-文件解析及上传(下)

随着互联网的迅速发展&#xff0c;网络安全问题变得日益复杂&#xff0c;而文件解析及上传漏洞成为攻击者们频繁攻击的热点之一。本文将深入研究文件解析及上传漏洞&#xff0c;通过对文件上传、Web容器IIS、命令执行、Nginx文件解析漏洞以及公猫任意文件上传等方面的细致分析&…

基于C/C++的非系统库自定义读写ini配置

INI文件由节、键、值组成。 节 [section] 参数 &#xff08;键值&#xff09; namevalue 这里将常用的操作方式封装成了一个dll供外部使用 // 下列 ifdef 块是创建使从 DLL 导出更简单的 // 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 LIBCFG_EXPORTS // 符号…

有什么简单实用的民宿管理系统软件?民宿如何开拓盈利渠道?

民宿是近些年兴起的一种小型住宿设施&#xff0c;一般就是利用当地的一些民房改造的。民宿的装修风格一般更加具有本土风味&#xff0c;很适合拍照打卡。不过民宿由于规模比较小&#xff0c;所以员工一般也不多&#xff0c;很多民宿的入住登记都要旅客自己弄。所以有很多民宿会…

STC8模板代码

目录 STC8依赖文件库 程序结构 GPIO操作 初始化所有 使用宏配置IO口 使用函数配置IO口 UART操作 头文件 初始化 UART1 UART2 UART3 UART4 接收逻辑 UART1 UART2 UART3 UART4 发送 UART1 UART2 UART3 UART4 配置printf Timer操作 导入依赖 初始化 Time…

Apache DolphinScheduler 社区荣获 “2023 年度优秀开源技术团队“ 奖项

在开源社区日益繁荣的今天&#xff0c;我们非常荣幸地宣布&#xff1a;Apache DolphinScheduler 社区在 OSCHINA 平台的评选中荣获了“2023 年度优秀开源技术团队”奖项。这一奖项反映了我们社区在过去一年里在内容发表的深度与广度、活动运营影响力以及对开源文化的推广方面所…

AGILE-SCRUM

一个复杂的汽车ECU开发。当时开发队伍遍布全球7个国家&#xff0c;10多个地区&#xff0c;需要同时为多款车型定制不同的软件&#xff0c;头疼的地方是&#xff1a; 涉及到多方人员协调&#xff0c;多模块集成和管理不同软件团队使用的设计工具、验证工具&#xff0c;数据、工…

JS-sessionStorage、localStorage和cookie

sessionStorage 仅在浏览器当前窗口关闭之前有效,即使是刷新或者进入该窗口下的的另一个页面&#xff0c;数据也仍然存在&#xff1b;关闭浏览器或到另一个窗口&#xff0c;数据就是不存在的。 // 设置sessionStorage保存到本地&#xff0c;第一个为变量名&#xff0c;第二个是…

实验一 门电路逻辑功能及测试

一、实验目的 1.熟悉门电路逻辑功能. 2.熟悉数字电路学习机及示波器使用方法。 二、实验仪器及材料 1.双踪示波器 2.器件 74LS00 二输入端四与非门 2片 74LS20 四输人端双与非门 1片 74LS86 二输入端四异或门 1片 74LS04 六反相器 1片 …

如何在本地搭建Oracle数据库并实现无公网ip通过PLSQL工具远程连接数据库

文章目录 前言1. 数据库搭建2. 内网穿透2.1 安装cpolar内网穿透2.2 创建隧道映射 3. 公网远程访问4. 配置固定TCP端口地址4.1 保留一个固定的公网TCP端口地址4.2 配置固定公网TCP端口地址4.3 测试使用固定TCP端口地址远程Oracle 正文开始前给大家推荐个网站&#xff0c;前些天发…

基于JAVA的校园电子商城系统论文

摘 要 网络技术和计算机技术发展至今&#xff0c;已经拥有了深厚的理论基础&#xff0c;并在现实中进行了充分运用&#xff0c;尤其是基于计算机运行的软件更是受到各界的关注。加上现在人们已经步入信息时代&#xff0c;所以对于信息的宣传和管理就很关键。因此校园购物信息的…