【C++】string类的增删改查模拟实现(图例超详细解析!!!)

马肤
这是懒羊羊

目录

一、前言

二、string类的模拟实现 

✨前情提要 

✨Member functions —— 成员函数

 ⚡构造函数

 ⚡拷贝构造函数

 ⚡赋值运算符重载

 ⚡析构函数

 ✨Element access —— 元素访问

⚡operator[ ]

⚡Iterator —— 迭代器

 ✨Capacity —— 容量

 ⚡size

 ⚡capacity

 ⚡clear

 ⚡empty

 ⚡reserve

 ⚡resize

 ✨Modifiers —— 修改器

 ⚡push_back

 ⚡append

 ⚡operator+=(char ch)

 ⚡perator+=(const char* s)

 ⚡insert 

 ⚡erase

 ⚡swap

✨String Operations —— 字符串操作 

 ⚡ find --- 寻找一个字符

 ⚡find --- 寻找一个字符串 

 ⚡substr

 ✨非成员函数重载

⚡relational operators 

⚡operator> 流提取【⭐】 

⚡getline 

 三、string 类的模拟实现整体代码

🥝 string.h

🍇string.cpp

🍍test.cpp

 四、共勉


一、前言

        在经过漫长的类和对象与STL 学习之后,对于 STL中的 string类有了一个基本的认识,如果还有不太了解string 类的老铁,可以看看这篇文章:string类的详解

        本模块呢,我将会带大家一起从 0~1去模拟实现一个STL库中的 string类,当然模拟实现的都是一些常用的接口,以便于让大家更好的巩固之前学习过的 缺省参数、封装、类中的6大默认成员函数等,代码量大概在 600行左右。

二、string类的模拟实现 

✨前情提要 

  • 首先第一点,为了不和库中的string类发生冲突,我们可以包上一个名称为xas_string的命名空间,此时因为作用域的不同,就不会产生冲突了,如果这一块有点忘记的同学可以再去看看 namespace命名空间
    #pragma once
    #include 
    #include 
    using std::ostream;
    using std::istream;
    using std::cout;
    using std::cin;
    using std::endl;
    // 为了不和 std库 中的 string类 发生冲突,创建我们自己的作用域xas_string
    namespace xas_string
    {
    	class string
    	{
    	public:
    		// .....
    	private:
    		char* _str;         // 指向字符数组的指针
    		size_t _size;       // 字符数组的有效数据的长度
    		size_t _capacity;   // 字符串数组的容量
    	};
    }
    • 接下去呢,就在string.cpp中进行定义,在test.cpp中进行测试即可。其中需要包含一下这个头文件,此时我们才可以在自己实现的类中去调用一些库函数
      #include "string.h"

      ✨Member functions —— 成员函数

       ⚡构造函数

      好,首先第一个我们要来讲的就是【构造函数】 

      • 首先我们从无参的构造函数开始讲起,看到下面的代码,你是否有想起了 C++初始化列表,我们默认给到 _size 和 _capacity 的大小为,然后给字符数组开了一个大小的空间,并且将其初始化为\0 在string.cpp定义中可以写为:
        // 无参构造函数
        // 在 xas_string作用域中的string类 的string()函数
        xas_string::string::string()
        	:_str(new char[1])    
        	, _size(0)
        	, _capacity(0)
        {
        	_str[0] = '
        xas_string::string s1;
        
        '; }
        • 然后我们立即在test.cpp中测试一下,因为我们自己实现的 string类 是包含在了命名空间xas_string中的,那么我们在使用这个类的时候就要使用到 域作用限定符::
          //  测试初始化,与循环打印 迭代器
          void test1()
          {
          	xas_string::string s1;
          	cout = _size)
          			{
          				_str[pos] = '

           ⚡swap

          '; _size = pos; } else { strcpy(_str + pos, _str + pos + len); _size -= len; } }
          void swap(string& s)
          {
          	std::swap(_str, s._str);
          	std::swap(_size, s._size);
          	std::swap(_capacity, s._capacity);
          }
          

            💬 马上我们就来分类测试一下

          ✨String Operations —— 字符串操作 

          • 对于【swap】函数我们在上面已经有讲解过了,此处不再过度赘述

            ⚡ find --- 寻找一个字符

            size_t find(char ch, size_t pos) const
            {
            	assert(pos  _size)
            {
            	// 就算要取再大的长度,也只能取到pos - _size的位置
            	n = _size - pos;
            }
            

            然后再来讲讲有关字符串的一些操作 

             ✨非成员函数重载

            • 这个很简单,就是去遍历一下当前对象中的_str,若是在遍历的过程中发现了字符ch的话就返回这个位置的下标,如果遍历完了还是没有找到的话就返回npos这个最大的无符号数

              ⚡relational operators 

              • 那接下去的话我们就可以去取这个子串了,使用循环的方式从pos位置开始取,取【n】个即可,然后追加到这个临时的 string对象 中去,最后呢再将其返回即可,那我们返回一个出了作用域就销毁的临时对象,只能使用【传值返回】,而不能使用【传引用返回】
                string tmp;
                tmp.reserve(n);
                for (size_t i = pos; i  
                

                //>>运算符的重载
                istream& operator>>(istream& in, string& s)
                {
                	s.clear(); //清空字符串
                	char ch = in.get(); //读取一个字符
                	while (ch != ' '&&ch != '\n') //当读取到的字符不是空格或'\n'的时候继续读取
                	{
                		s += ch; //将读取到的字符尾插到字符串后面
                		ch = in.get(); //继续读取字符
                	}
                	return in; //支持连续输入
                }
                

                 最后的话再来模拟一些【非成员函数重载】,使用到的也是非常多

                ⚡getline 

                ① 小于 

                //运算符直接输入。输入前我们需要先将对象的C字符串置空,然后从标准输入流读取字符,直到读取到’ ‘或是’\n’便停止读取。
                
                //读取一行含有空格的字符串
                istream& getline(istream& in, string& s)
                {
                	s.clear(); //清空字符串
                	char ch = in.get(); //读取一个字符
                	while (ch != '\n') //当读取到的字符不是'\n'的时候继续读取
                	{
                		s += ch; //将读取到的字符尾插到字符串后面
                		ch = in.get(); //继续读取字符
                	}
                	return in;
                }
                

                 💬 马上我们就来分类测试一下

                 三、string 类的模拟实现整体代码

                getline函数用于读取一行含有空格的字符串。实现时于>>运算符的重载基本相同,只是当读取到’\n’的时候才停止读取字符。 

                🥝 string.h

                 💬 马上我们就来分类测试一下

                #pragma once
                #include 
                #include 
                using std::ostream;
                using std::istream;
                using std::cout;
                using std::cin;
                using std::endl;
                // 为了不和 std库 中的 string类 发生冲突,创建我们自己的作用域
                namespace xas_string
                {
                	class string
                	{
                	public:
                		typedef char* iterator;             // 迭代器某种意义上就是 指针
                		typedef const char* const_iterator; 
                		//  默认成员函数
                		string(const char* str = "");       // 有参构造函数
                		string(const string& s);            // 拷贝构造
                		string& operator=(const string& s); // 赋值运算符重载
                		~string();                          // 析构函数
                		//迭代器相关函数
                		iterator begin();
                		iterator end();
                		const_iterator begin() const;
                		const_iterator end() const;
                		//容量和大小相关函数
                		size_t size()  const;                 // 返回目前 字符串的有效字符个数
                		size_t capacity() const;              // 返回目前 字符串的容量
                		void clear();                         // 清空字符串
                		bool empty() const;                   // 判断字符串是否为空
                		void reserve(size_t newCapacity = 0); // 扩容(修改_capacity)
                		void resize(size_t newSize, char c = '

                 🍇string.cpp

                '); // 改变大小 // 修改字符串相关函数 void push_back(char ch); // 追加一个字符 void swap(string& s); // 交换 --- 交换两个字符串 void append(const char* s); // 追加一个字符串 string& operator+=(char ch); // 追加一个字符 string& operator+=(const char* s); // 追加一个字符串 void insert(size_t pos, const char* str); // 插入n个字符 void erase(size_t pos, size_t len = npos); // 删除 -- 从 pos 位置 删除 长度为 len 的字符串 // 访问字符串相关函数 char& operator[](size_t pos); // 可读可写 const char& operator[](size_t pos) const; // 可读不可写 const char* c_str()const; // 用 C语言的方式返回 size_t find(char ch, size_t pos = 0); // 寻找字符 size_t find(const char* str, size_t pos = 0); // 寻找 字符串 string substr(size_t pos = 0, size_t len = npos); // 截取字符串 从某个位置 取 len 个字符 // 关系运算符重载 bool operator=(const string& s); // >=运算符重载 bool operator!=(const string& s); // !=运算符重载 private: char* _str; // 指向字符数组的指针 size_t _size; // 字符数组的有效数据的长度 size_t _capacity; // 字符串数组的容量 const static size_t npos = -1; //静态成员变量(整型最大值) }; // 流插入 ostream& operator运算符的重载 istream& operator>>(istream& in, string& s); //读取一行含有空格的字符串 istream& getline(istream& in, string& s); void print_str(const string& s); // const对象的输出 }
                #include "string.h"
                // 有参构造函数
                xas_string::string::string(const char* str)    // ""  --- 为空的字符串
                {
                	_str = new char[strlen(str) + 1];   // strlen 计算的是字符产的长度 ,不计算'' 所以要+1
                	_size = strlen(str);
                	_capacity = strlen(str);           // capacity 不包括 ''
                	strcpy(_str, str);
                }
                //拷贝构造
                xas_string::string::string(const string& s)
                	:_str(nullptr)
                	, _size(0)
                	, _capacity(0)
                {
                	string tmp(s._str); //调用构造函数,构造出一个C字符串为s._str的对象
                	swap(tmp); //交换这两个对象
                }
                // 赋值运算符重载
                // 传统写法
                //xas_string::string& xas_string::string::operator=(const string& s)
                //{
                //	if (this != &s)
                //	{
                //		char* tmp = new char[s._capacity + 1];
                //		//memcpy(tmp, s._str, s._size + 1);
                //		strcpy(tmp, s._str);
                //		delete[] _str;
                //
                //		_str = tmp;
                //		_size = s._size;
                //		_capacity = s._capacity;
                //	}
                //	return *this;
                //}
                //现代写法2
                xas_string::string& xas_string::string::operator=(const string& s)
                {
                	if (this != &s) //防止自己给自己赋值
                	{
                		string tmp(s); //用s拷贝构造出对象tmp
                		swap(tmp); //交换这两个对象
                	}
                	return *this; //返回左值(支持连续赋值)
                }
                // 析构函数
                xas_string::string::~string()
                {
                	delete[] _str;
                	_str = nullptr;
                	_size = 0;
                	_capacity = 0;
                }
                char& xas_string::string::operator[](size_t pos)        // 可读可写
                {
                	assert(pos 

文章版权声明:除非注明,否则均为VPS857原创文章,转载或复制请以超链接形式并注明出处。

发表评论

快捷回复:表情:
评论列表 (暂无评论,0人围观)

还没有评论,来说两句吧...

目录[+]

取消
微信二维码
微信二维码
支付宝二维码