目录
一、前言
二、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
- 那接下去的话我们就可以去取这个子串了,使用循环的方式从pos位置开始取,取【n】个即可,然后追加到这个临时的 string对象 中去,最后呢再将其返回即可,那我们返回一个出了作用域就销毁的临时对象,只能使用【传值返回】,而不能使用【传引用返回】
- 这个很简单,就是去遍历一下当前对象中的_str,若是在遍历的过程中发现了字符ch的话就返回这个位置的下标,如果遍历完了还是没有找到的话就返回npos这个最大的无符号数
- 对于【swap】函数我们在上面已经有讲解过了,此处不再过度赘述
- 然后我们立即在test.cpp中测试一下,因为我们自己实现的 string类 是包含在了命名空间xas_string中的,那么我们在使用这个类的时候就要使用到 域作用限定符::
- 首先我们从无参的构造函数开始讲起,看到下面的代码,你是否有想起了 C++初始化列表,我们默认给到 _size 和 _capacity 的大小为,然后给字符数组开了一个大小的空间,并且将其初始化为\0 在string.cpp定义中可以写为:
- 接下去呢,就在string.cpp中进行定义,在test.cpp中进行测试即可。其中需要包含一下这个头文件,此时我们才可以在自己实现的类中去调用一些库函数
还没有评论,来说两句吧...