C++string的模拟实现(上篇)

news2024/9/20 1:00:26

目录

一.命名空间的封装与交换函数模板

1.命名空间的封装与类的定义

2.交换函数模板 

二.string类的四个重要默认成员函数

1.构造函数的类外定义:

2.析构函数在类外的定义

3.拷贝构造函数在类外的定义

4.赋值运算符重载在类外的定义 

5.关于两个string对象的内容交换问题

三.string类的迭代器 

四.string类的[]成员重载


一.命名空间的封装与交换函数模板

1.命名空间的封装与类的定义

模拟实现的string类最好用一个命名空间封装起来避免发生命名冲突。

命名空间定义:

namespace mystring
{
    //.....
}

类的定义(为了方便整理和维护成员方法的声明和定义分离):

(本期不含增删查改的接口)


namespace mystring
{
    class string
    {
        public:
            typedef char * iterator;                  //宏定义string类的迭代器
            typedef const char* const_iterator;



            string(const char* nstr);                  //类中四个重要的默认成员函数
            string(const string& nstr);
            ~string();
            string& operator=(string nstr);


            void copyswap(string &nstr);               //复用交换函数的接口
            iterator begin();                          //string类用于获取迭代器的接口
            iterator end();
            const_iterator begin()const;
            const_iterator end()const;
            const char * C_str()const;                 //用于返回C类型字符串的函数
            char & operator[](int pos);                //用于返回pos下标字符的引用的[]重载
            const char & operator[](int pos) const;     
            size_t size() const;                       //获取有效字符个数size和容量的接口
            size_t capacity() const;
        private:
            char * _str;
            size_t _size;
            size_t _capacity;
    };

}

2.交换函数模板 

template <typename T>
void myswap (T& e1 , T&e2)
{
   T tem = e1;
   e1 = e2;
   e2 =tem;
}

该模板可以根据传入的实参类型生成用于交换对应类型数据的函数实例。

二.string类的四个重要默认成员函数

1.构造函数的类外定义:

类外声明方法时注意表明方法所在命名空间和类域

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

  • 设计缺省参数是为了保证类的构造函数可以被无参调用,(形参缺省值用于构造空字符串)(会完成'\0'的拷贝)(注意参数缺省值只能写在函数的声明和定义其中一个之中)
  • strlen函数中有关于空指针的断言,所以构造函数中无须进行空指针断言
  •  +1是为了保存'\0'(不计入容量和有效字符),同时保证_str不为空指针(对象只要创建出来就一定要维护一块堆空间,防止析构的时候程序崩溃)

2.析构函数在类外的定义

mystring::string::~string()
{
    if(_str)
    {
        delete[] _str;      释放堆区空间
        _str=nullptr;
    }
    _size=0;
    _capacity=0;
}

3.拷贝构造函数在类外的定义

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

这是一种非复用的写法,现代STL中比较喜欢使用复用的写法,拷贝构造函数可以通过复用构造函数来实现,从而使代码更加简洁

现在类中定义一个成员交换函数(该函数通过复用swap函数实现):

    //template <typename T>
    //void myswap (T& e1 , T&e2)
    //{
    //   T tem = e1;
    //   e1 = e2;
    //   e2 =tem;
    //}
    void mystring::string::copyswap(string & nstr)
    {
        myswap(_str,nstr._str);
        myswap(_size,nstr._size);
        myswap(_capacity,nstr._capacity);
    }

调用该成员函数可以将两个string对象的各个成员变量进行交换

复用构造函数实现拷贝构造函数:

    mystring::string::string(const string& nstr)     //复用构造函数完成拷贝构造
    :_str (nullptr)                                  //防止tem中_str作为野指针被释放
    ,_size(0)
    ,_capacity(0)
    {
        string tem(nstr._str);                       //拷贝构造拷贝的是不含'\0'的有效字符
        this->copyswap(tem);                         //为了方便阅读加上this指针
    }

该实现方法的思想是通过形参对象中的字符串构造tem临时对象,再将tem对象中的内容通过交换函数置换到被构造的对象中去。

tem在函数体执行完后会自动调用自身的析构函数并销毁

4.赋值运算符重载在类外的定义 

   string& mystring::string::operator=(const string& nstr)
   {
       if(this != &nstr)                           //判断重载操作数是否为同一个对象
       {
          char * tem = new char[nstr._capacity+1];  
          strcpy(tem,nstr._str);
          delete[] _str;                           //释放原来的堆区空间
          _str = tem;
          _size = nstr._size;
          _capacity = nstr._capacity;
        }
        return *this;
   }
  • 注意不能直接用_str去接收新申请的堆区空间地址(因为new可能会失败)
  • 注意返回对象本身的引用*this(使=重载可以满足连等赋值)

赋值运算符重载也可以通过复用拷贝构造来实现:

    string& mystring::string::operator=(string nstr)     
    {
        copyswap(nstr);
        return (*this);
    }
  • 复用拷贝构造实现赋值运算符重载的思路:

5.关于两个string对象的内容交换问题

  • STL的标准库中string类中有成员交换函数swap(相当于本篇模拟实现中的copyswap成员函数),使用成员函数完成两个string对象的内容交换效率远高于直接使用全局swap函数(相当于本篇模拟实现中的myswap函数)完成两个string对象的内容交换
  • 直接调用全局交换函数交换两个string对象额外调用对象的拷贝构造函数和赋值运算符重载从而增加性能消耗(new申请空间的性能消耗是很大的)

比如:

using mystring::string;
using mystring::myswap;



int main()
{
    string a("hello");
    string b("world");
    
    myswap(a,b);    交换方式一(直接调用全局交换函数)
    
    a.copyswap(b);  交换方式二(调用成员交换函数)
    
    return 0;
    
}

两种对象交换的方式差异图解:

 

三.string类的迭代器 

string类的迭代器可以typedef为字符指针

    mystring::string::iterator mystring::string:: begin()
    {
        return _str;
    }
    mystring::string::iterator mystring::string::end()
    {
        return _str+_size; 
    }
    mystring::string::const_iterator mystring::string::begin()const
    {
        return _str;
    }
    mystring::string::const_iterator mystring::string::end()const
    {
        return _str+_size;
    }

四.string类的[]成员重载

string类的[]成员重载用于返回字符串中指定下标的字符的引用

    char &  mystring::string::operator[](int pos)
    {
        assert(pos<_size);
        return _str[pos];
    }
    const char &  mystring::string::operator[](int pos) const
    {
        assert(pos<_size);
        return _str[pos];
    }
  • const修饰的重载成员是供const修饰的string对象调用的

完整代码:

#include<iostream>
#include<cstring>
#include <assert.h>
//只要不涉及寻址,一定要注意编译器的顺序编译机制

namespace mystring
{



    class string
    {
        public:
            typedef char * iterator; 
            typedef const char* const_iterator;



            string(const char* nstr);                  //类中四个重要的默认成员函数
            string(const string& nstr);
            ~string();
            string& operator=(string nstr);


            void copyswap(string &nstr);               //复用交换函数的接口
            iterator begin();                          //string类用于获取迭代器的接口
            iterator end();
            const_iterator begin()const;
            const_iterator end()const;
            const char * C_str()const;                 //用于返回C类型字符串的函数
            char & operator[](int pos);                //用于返回pos下标字符的引用的[]重载
            const char & operator[](int pos) const;     
            size_t size() const;                       //获取有效字符个数size和容量的接口
            size_t capacity() const;
        private:
            char * _str;
            size_t _size;
            size_t _capacity;
    };

    
    
    template <typename T>
    void myswap (T& e1 , T&e2)
    {
        T tem = e1;
        e1 = e2;
        e2 =tem;
    }
    void mystring::string::copyswap(string & nstr)
    {
        myswap(_str,nstr._str);
        myswap(_size,nstr._size);
        myswap(_capacity,nstr._capacity);
    }
    const char * mystring::string::C_str() const
    {
        return _str;
    }
    

    mystring::string::string(const char* nstr = "")  //缺省值用于构造空字符串(会完成'\0'的拷贝)(注意缺省参数只能写在声明和定义其中一个之中)
    :_size(strlen(nstr))                             //strlen函数中有关于空指针的断言,所以构造函数中无须进行空指针判断
    ,_capacity (_size)
    {                          
        _str = new char[_capacity+1];                // +1是为了保存'\0'(不计入容量和有效字符),同时保证_str不为空指针(对象只要创建出来就一定维护一块堆空间)
        strcpy(_str,nstr);
    }
    // mystring::string::string(const string& nstr)  //非复用式写法
    // :_size(nstr._size)
    // ,_capacity(nstr._capacity)
    // {
    //     _str = new char[_capacity+1];             // +1是为了保存'\0'(不计入容量和有效字符),同时保证_str不为空指针
    //     strcpy(_str,nstr._str);
    // }
    mystring::string::string(const string& nstr)     //复用构造函数完成拷贝构造
    :_str (nullptr)                                  //防止tem中_str作为野指针被释放
    ,_size(0)
    ,_capacity(0)
    {
        string tem(nstr._str);                       //拷贝的是不含'\0'的有效字符
        this->copyswap(tem);                         //为了方便阅读加上this指针
    }


    // string& mystring::string::operator=(const string& nstr)
    // {
    //     if(this != &nstr)                             //判断重载操作数是否为同一个对象
    //     {
    //         char * tem = new char[nstr._capacity+1];  // +1是为了保存'\0'(不计入容量和有效字符)
    //         strcpy(tem,nstr._str);
    //         delete[] _str;
    //         _str = tem;
    //         _size = nstr._size;
    //         _capacity = nstr._capacity;
    //     }
    //     return *this;
    // }
    string& mystring::string::operator=(string nstr)     //与直接交换对象作对比
    {
        copyswap(nstr);
        return (*this);
    }
    mystring::string::~string()
    {
        if(_str)
        {
            delete[] _str;
            _str=nullptr;
        }
        _size=0;
        _capacity=0;
    }




    mystring::string::iterator mystring::string:: begin()
    {
        return _str;
    }
    mystring::string::iterator mystring::string::end()
    {
        return _str+_size; 
    }
    mystring::string::const_iterator mystring::string::begin()const
    {
        return _str;
    }
    mystring::string::const_iterator mystring::string::end()const
    {
        return _str+_size;
    }




    char &  mystring::string::operator[](int pos)
    {
        assert(pos<_size);
        return _str[pos];
    }
    const char &  mystring::string::operator[](int pos) const
    {
        assert(pos<_size);
        return _str[pos];
    }


    size_t mystring::string::size() const
    {
        return _size;
    }
    size_t mystring::string::capacity() const
    {
        return _size;
    }

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

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

相关文章

在线 OJ 项目(一) · 项目介绍 · 进程与线程 · 实现编译运行模块

一、项目介绍二、导入依赖、创建基本项目结构导入依赖创建基本项目结构三、进程、线程的基础知识回顾四、封装操作进程的工具类五、实现 “编译运行” 模块 Task 类六、封装读写文件的方法修改 JDK 版本七、Task 类的实现八、整理一下项目列表一、项目介绍 项目实现一个在线 O…

煤矿AI智能视频分析识别系统 opencv

煤矿AI智能视频分析识别系统通过opencvpython 深度学习网络模型&#xff0c;对皮带跑偏、撕裂、堆煤、异物、非法运人、有煤无煤状态等异常情况&#xff0c;以及人员工服穿戴、反光衣、安全帽、睡岗离岗、打电话、抽烟等行为进行自动抓拍存档。OpenCV基于C实现&#xff0c;同时…

【正点原子FPGA连载】第二十八章Linux并发与竞争 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Linux开发指南

1&#xff09;实验平台&#xff1a;正点原子MPSoC开发板 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id692450874670 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/thread-340252-1-1.html 第二十八章Linux…

【splishsplash】Houdini粒子的导入与导出

Houdini粒子的导入与导出 Houdini导入到splish 在Houdini中使用file导入任意几何模型 使用points from volume采样点&#xff0c;使其粒子化 使用file导出粒子化之后的模型&#xff0c;后缀写bhclassic 创建json场景文件&#xff08;建议放到MyScences文件夹&#xff09; …

C语言指针是什么?

计算机中所有的数据都必须放在内存中&#xff0c;不同类型的数据占用的字节数不一样&#xff0c;例如 int 占用 4 个字节&#xff0c;char 占用 1 个字节。为了正确地访问这些数据&#xff0c;必须为每个字节都编上号码&#xff0c;就像门牌号、身份证号一样&#xff0c;每个字…

HTTP协议 | 青训营笔记

1、概述 HTTP协议&#xff0c;超文本传输协议 应用层的协议&#xff0c;基于TCP协议&#xff0c;简单可扩展&#xff08;可以自定义header&#xff09; 每个HTTP请求都可以分为请求和响应两个部分 无状态的&#xff08;不知道之前的请求是携带过什么信息&#xff09; 2、协…

【Qt】8.QPainter、高级设置、手动调用绘图事件、绘图设备、文件操作、文件信息

目录 QPainter 代码 widget.h widget.cpp 结果 高级设置 代码 widget.h widget.cpp 结果 手动调用绘图事件 代码 widget.h widget.cpp 结果 绘图设备 代码 widget.h widget.cpp 结果 文件操作 代码 widget.cpp 结果 文件信息 代码 widget.cpp 结果…

一文带你看懂健康管理系统----IPMI

目录 1. IPMI概述 2. IPMI系统设计 3. 主BMC模块设计 5. 从IPMI模块设计 6. 名词解释 6. 代码 1. IPMI概述 智能平台管理接口&#xff08;IPMI&#xff1a;Intelligent Platform Management Interface&#xff09;是一项应用于服务器管理系统设计的标准&#xff0c;由Int…

BSN-DDC基础网络详解(一):基础介绍

BSN-DDC基础网络推出已经一年了&#xff0c;得到了行业应用方和广大开发者的高度认可。一年中BSN产品技术团队也在根据市场业务需求不断更新功能服务&#xff0c;我们将通过本系列文章为大家系统化介绍DDC网络的功能和使用&#xff0c;为感兴趣的朋友提供学习帮助。BSN-DDC基础…

历史与今日的事件

作者简介&#xff1a;一名在校云计算网络运维学生、每天分享网络运维的学习经验、和学习笔记。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 目录 前言 1.2023年1月31日中国最新量子计算机“悟空”即将问世&#xff0c…

【34】C语言 | 动态内存管理

目录 1.为什么存在动态内存分配 2、动态内存函数的介绍 2.1 malloc和free 2.2 calloc 2.3 realloc 1.为什么存在动态内存分配 我们已经掌握的内存开辟方式有: int val 20; //在栈空间上开辟四个字节 char arr[n] {0}; //在栈空间上开辟10个节的连续空间 但是上述的开辟…

mPEG-SS-NHS甲氧基聚乙二醇-二硫键-琥珀酰亚胺酯

mPEG-SS-NHS甲氧基聚乙二醇-双硫键-活性酯 名称&#xff1a;甲氧基聚乙二醇-双硫键-琥珀酰亚胺酯 英文名称&#xff1a;mPEG-SS-NHS 存储条件&#xff1a;-20C&#xff0c;避光&#xff0c;避湿 用 途&#xff1a;仅供科研实验使用&#xff0c;不用于诊治 外观: 固体或粘性…

【c语言进阶】动态通讯录

&#x1f680;write in front&#x1f680; &#x1f4dc;所属专栏&#xff1a; c语言学习 &#x1f6f0;️博客主页&#xff1a;睿睿的博客主页 &#x1f6f0;️代码仓库&#xff1a;&#x1f389;VS2022_C语言仓库 &#x1f3a1;您的点赞、关注、收藏、评论&#xff0c;是对我…

彭博:预订量未及预期,索尼大幅削减PS VR2首季订单

在索尼VR新品&#xff1a;PS VR2正式发货前夕&#xff0c;彭博社爆料称&#xff1a;因预订量不及预期&#xff0c;索尼已经大幅削减PS VR2首季订单量。消息人士称&#xff0c;索尼PS VR2发布后首个季度的订单目标是200万台&#xff0c;现已根据预订量减半&#xff0c;至约100万…

iOS 视频播放器开发

需求设计 做一个小学生教育辅导视频播放器。 参考小猿搜题视频播放器 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L0GsyFSt-1675164972791)(https://tva1.sinaimg.cn/large/008vxvgGgy1h9xk4fm5xfj31sx0u0mz0.jpg)] [外链图片转存失败,源站可…

Java中的JCA对基于密码的加密进行成功的蛮力搜索攻击所需的时间估计

对于JCA中基于密码的DES加密实现&#xff0c;固定一些盐和迭代计数&#xff0c;并记录加密/解密所需的平均时间;估计成功进行蛮力搜索攻击所需的时间Name: NIHAO DONG Number: 201476606 Password List:N Password 1 P$$W0rD 2 thisismypassword 3 VeryLongP$$W0rD fi…

[0CTF 2016]piapiapia(字符逃逸详解)

目录 知识点 信息收集 尝试SQL注入 源码目录扫描 代码审计 payload生成 知识点 信息泄露参数传递数组绕过字符串检测反序列化字符逃逸 信息收集 收集到了一只超可爱的小喵 尝试SQL注入 用户名不存在的回显 密码无效的回显 用户存在&#xff0c;密码错误的回显 判断闭…

STC15系列PWM功能相关功能寄存器介绍

STC15系列PWM功能相关功能寄存器介绍✨以下数据来源于stc15手册。 &#x1f4d3;增强型PWM波形发生器相关功能寄存器总表 1. 端口配置寄存器&#xff1a;P_SW2 2.PWM配青寄存器:PWMICFG CBTADC: PWM计数器归零时 (CBIF1时) 触发ADC转换 – 0:PWM计数器归零时不触发ADC转换 – …

Web3中文|亚马逊进入web3,将在春季推出NFT计划

亚马逊正向加密行业迈出第一步。 根据Blockworks 1月26日发布的报告&#xff0c;这家电子商务巨头计划在2023年春天推出一项专注于区块链游戏和相关NFT的计划。 该计划仍处于开发阶段&#xff0c;但发布的最后期限定为4月。亚马逊用户将可以体验基于区块链的游戏并领取免费的…

Python采集某乎专栏文章保存成pdf

前言 大家早好、午好、晚好吖 ❤ ~ 环境使用: Python 3.8 Pycharm wkhtmltopdf 软件 --> 文章下方名片信领取 模块使用: requests >>> pip install requests 数据请求 parsel >>> pip install parsel 数据解析 re >>> 内置模块 不需要安装…