当前位置: 首页 > news >正文

C++ -- string类的模拟实现

       //     欢迎来到 aramae 的博客,愿 Bug 远离,好运常伴!  //

博主的Gitee地址:阿拉美 (aramae) - Gitee.com

时代不会辜负长期主义者,愿每一个努力的人都能达到理想的彼岸。

  • string类的模拟实现
  • 扩展阅读

引言: 本章模拟实现string类,包括string 的构造函数,析构函数的部分接口,等到C++学习到一定程度上后会逐渐补充STL源码剖析书中的内容,敬请期待。另外补充部分扩展阅读。


1.string类的模拟实现 

注意:本部分的string的模拟实现是根据string标准库来逐步实现的,目的不是为了实现一个更好的string类,只是通过其底层的实现,加深对string的理解,只实现部分重要的函数。

这里的实现顺序和上一章介绍的顺序一致,避免混乱;文章末尾会给出完整的实现代码+测试代码通过本章的模拟,加深类和对象的理解和运用,如有遗忘,请及时复习。

C++ string 类(标准库中的string类)-CSDN博客

C++ 类和对象(上)(类的定义,类的访问限定及封装...)-CSDN博客

C++ 类和对象(中) (默认成员函数,构造函数,析构函数......)-CSDN博客

C++ 类和对象(下)(初始化列表,static成员,友元......)-CSDN博客


一.成员函数(Member functions):

1.1 构造函数 construcotrs

  • string();

  • string(const char* str);

  •  char* c_str();

  • string(const string& str);

这里我们的模拟实现包含 无参默认构造,全缺省构造,拷贝构造函数

代码实现如下:

//我们知道默认构造函数包括三部分://无参构造函数,全缺省构造函数,编译器自己生成的默认构造//第一种:无参构造(实现)string():_size(0),_capacity(0),_str(new char[1]{'\0'}){ }//C++风格:string() : _size(0), _capacity(0), _str(new char[1] {'\0'}) {}// 第二种:全缺省构造函数(实现)string(const char* str=""):_size(strlen(str)), _capacity(_size), _str(new char[_capacity + 1]){strcpy(_str, str);}//拷贝构造函数string(const string& s){_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}

那么看上面的代码,你会发现我们使用string标准库里的默认构造后初始化后的字符串是没有 ‘\0’的但这里的实现是在初始化后结尾加‘\0’的,为什么?这里是为了简化了 c_str() 的实现(无需动态添加 '\0'),通过预分配 1 字节存储 '\0',确保 _str 始终是合法的 C 风格字符串自定义实现的选择:这里的代码选择了一种保守实现,确保 _str 始终指向一个合法的 C 风格字符串。

总结:自定义 string 类的默认构造函数和标准库 std::string 的行为差异源于实现方式的不同

其中差异体现在

  • 自定义实现需要分配空间并存储 '\0'
  • 标准库的 std::string 在默认构造后不包含 '\0'

标准库的优化std::string 追求效率,在空字符串时不存储任何字符,仅在调用 c_str() 时动态添加 '\0'。这样可以节省内存并减少初始化开销

那么我们 c_str 的实现就变得十分容易简洁:

		//实现c_str接口const char* c_str() const{return _str;}

可以看一下具体的差异体现: 

当然,你也可以模拟标准库的实现,但是C 风格接口兼容性需额外处理

  • 若直接传递空字符串的指针(未调用 c_str()),可能导致野指针问题,需确保接口正确处理

下面是模仿标准库的实现:

class string {
private:char* _str;size_t _size;size_t _capacity;public:string() : _str(nullptr), _size(0), _capacity(0) {}  // 初始化为空指针~string() {delete[] _str;  // 处理 nullptr 的情况}const char* c_str() const {if (_size == 0) return "";  // 空字符串返回静态常量 ""return _str;}
};

对应的 c_str 接口:

const char* c_str() const {if (_size == 0) {return "";  // 返回静态常量 ""(即 "\0")}return _str;    // 非空字符串直接返回内部指针}

    1.2  析构函数destructor 

    • ~string(); 

      //析构函数~string(){delete[] _str;_str = nullptr;_capacity = _size = 0;}

      1.3 operator= 

    • string& operator=(const string& str) 

    传统写法:

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

    现代写法:

    	void swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}/*string& operator=(const string& s){if (this != &s){string tmp(s);//this->swap(tmp);swap(tmp);}return *this;}*/string& operator=(string tmp){swap(tmp);return *this;}

    二. string遍历操作(iterators):

     这里简单实现一下就行

    //迭代器 + 遍历操作(含下标[]访问)接口typedef char* iterator;typedef const char* const_iterator;iterator begin(){return _str;}const_iterator begin()const{return _str;}iterator end(){return _str + _size;}const_iterator end() const{return _str + _size;}size_t size()const{return _size;}char& operator[](size_t pos){assert( pos < _size);return _str[pos];}const char& operator[](size_t pos) const{assert(pos < _size);return _str[pos];}
    void test2()
    {aramae::string s1("hello world!!!");for (size_t i = 0; i < s1.size(); i++){cout << s1[i] << " ";}cout << endl;const aramae::string s3("aramae");//aramae::string::const_iterator cit = s3.begin();auto cit = s3.begin();while (cit != s3.end()){cit += 1;cout << *cit << " ";++cit;}cout << endl;aramae::string::iterator it = s1.begin();while (it != s1.end()){*it += 1;cout << *it << " ";++it;}cout << endl;for (auto ch : s1){cout << ch << " ";}cout << endl;}

    三.  string类对象的容量操作 (Capacity):

    • size_t size()const;
    		size_t size()const{return _size;}
    • void resize(size_t n, char ch = '\0');
    void resize(size_t n, char ch = '\0'){if (n < _size){_size = n;_str[_size] = '\0';}else{reserve(n);for (size_t i = _size; i < n; i++){_str[i] = ch;}_size = n;_str[_size] = '\0';}}
    • void reserve(size_t n);
    void reserve(size_t n){if (n > _capacity){cout << "reserve()->" << n << endl;char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}
    • void clear();
    		void clear(){_str[0] = '\0';_size = 0;}

    四.  string类对象的修改操作(Modifiers):

    • void push_back(char ch);
    		void push_back(char ch){if (_size == _capacity){//2倍扩容reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = ch;++_size;_str[_size] = '\0';}
    • void append(const char* str);
    void append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){//至少扩容到_size+lenreserve(_size + len);}strcpy(_str + _size, str);_size += len;}
    • string& operator+=(char ch);
    		string& operator+=(char ch) {push_back(ch);return *this;}
    • string& operator+=(const char* str);
    		string& operator+=(const char* str){append(str);return *this;}
    • void insert(size_t pos, size_t n, char ch);
    void insert(size_t pos, size_t n, char ch){assert(pos <= _size);if (_size + n > _capacity){//至少扩容到_size+lenreserve(_size + n);}//挪动数据/*	int end = _size;while (end >= (int)pos){_str[end + n] = _str[end];--end;}*/size_t end = _size;while (end >= pos && end != npos){_str[end + 1] = _str[end];--end;}for (size_t i = 0; i < n; i++){_str[pos + 1] = ch;}_size += n;}
    • void insert(size_t pos, const char* str);
    void insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);if (_size + len > _capacity){//至少扩容到_size+lenreserve(_size + len);}size_t end = _size;while (end >= pos && end != npos){_str[end + len] = _str[end];--end;}for (size_t i = 0; i < len; i++){_str[pos + i] = str[i];}_str += len;}
    • void erase(size_t pos, size_t len = npos);
    void erase(size_t pos, size_t len = npos){assert(pos <= _size);if (len == npos || pos + len >= _size){_size = pos;_str[_size] = '\0';}else{size_t end = pos + len;while (end <= _size){_str[pos++] = _str[end++];}_size -= len;}}
    • size_t find(char ch, size_t pos = 0);
    		size_t find(char ch, size_t pos = 0){assert(pos < _size);for (size_t i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return npos;}
    • size_t find(const char* str , size_t pos = 0);
    size_t find(const char* str, size_t pos = 0){assert(pos < _size);const char* ptr = strstr(_str + pos, str);if (ptr){return ptr - _str;}else{return npos;}}
    • string substr(size_t pos = 0, size_t len = npos);
    		string substr(size_t pos = 0, size_t len = npos){assert(pos < _size);size_t n = len;if (len == npos || pos + len > _size){n = _size - pos;}string tmp;tmp.reserve(n);for (size_t i = pos; i < pos + n; i++){tmp += _str[i];}return tmp;}
    

    五. string类非成员函数(Non-member function overloads):

    • bool operator<(const string& s) const;
    bool operator<(const string& s) const{int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);return ret == 0 ? _size < s._size : ret < 0;}
    • bool operator==(const string& s) const;
    	bool operator==(const string& s) const{return _size == s._size&& memcmp(_str, s._str, _size) == 0;}
    
    • bool operator<=(const string& s) const;
    bool operator<=(const string& s) const{return *this < s || *this == s;}
    • bool operator>(const string& s) const;
    bool operator>(const string& s) const{return !(*this <= s);}
    
    • bool operator>=(const string& s) const;
    		bool operator>=(const string& s) const{ return !(*this < s);}
    • bool operator!=(const string& s) const;
    bool operator!=(const string& s) const{ return !(*this == s);}
    • ostream& operator<<(ostream& out, const string& s);
    ostream& operator<<(ostream& out, const string& s){/*for (size_t i = 0; i < s.size(); i++){out << s[i];}*/for (auto ch : s){out << ch;}return out;}
    • istream& operator>>(istream& in, string& s);

    istream& operator>>(istream& in, string& s){s.clear();char ch = in.get();// 处理前缓冲区前面的空格或者换行while (ch == ' ' || ch == '\n'){ch = in.get();}//in >> ch;char buff[128];int i = 0;while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == 127){buff[i] = '\0';s += buff;i = 0;}//in >> ch;ch = in.get();}if (i != 0){buff[i] = '\0';s += buff;}return in;}
    

    string.h .cpp

    #pragma once
    #define _CRT_SECURE_NO_WARNINGS
    #include<assert.h>
    #include<iostream>
    using namespace std;namespace aramae
    {class string {public://我们知道默认构造函数包括三部分://无参构造函数,全缺省构造函数,编译器自己生成的默认构造//第一种:无参构造(实现)/*	string():_size(0),_capacity(0),_str(new char[1]{'\0'}){ }*///C++风格:string() : _size(0), _capacity(0), _str(new char[1] {'\0'}) {}// 第二种:全缺省构造函数(实现)string(const char* str=""):_size(strlen(str)), _capacity(_size), _str(new char[_capacity + 1]){strcpy(_str, str);}//实现c_str接口const char* c_str() const{return _str;}// 转换构造函数//string(const char* str)//	:_size(strlen(str))//	, _capacity(_size)//	, _str(new char[_capacity + 1])//{//	strcpy(_str, str);//}//拷贝构造函数string(const string& s){_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}//析构函数~string(){delete[] _str;_str = nullptr;_capacity = _size = 0;}string& operator=(const string& s){if (this == &s){return *this;}delete[] _str;_size = s._size;_capacity = s._capacity;_str = new char[_capacity + 1];strcpy(_str, s._str);}//void swap(string& s)//{//	std::swap(_str, s._str);//	std::swap(_size, s._size);//	std::swap(_capacity, s._capacity);//}//string& operator=(const string& s)//{//	if (this != &s)//	{//		string tmp(s);//		//this->swap(tmp);//		swap(tmp);//	}//	return *this;//}//string& operator=(string tmp)//{//	swap(tmp);//	return *this;//}//迭代器 + 遍历操作(含下标[]访问)typedef char* iterator;typedef const char* const_iterator;iterator begin(){return _str;}const_iterator begin()const{return _str;}iterator end(){return _str + _size;}const_iterator end() const{return _str + _size;}size_t size()const{return _size;}char& operator[](size_t pos){assert( pos < _size);return _str[pos];}const char& operator[](size_t pos) const{assert(pos < _size);return _str[pos];}//string 容器操作void resize(size_t n, char ch = '\0'){if (n < _size){_size = n;_str[_size] = '\0';}else{reserve(n);for (size_t i = _size; i < n; i++){_str[i] = ch;}_size = n;_str[_size] = '\0';}}void reserve(size_t n){if (n > _capacity){cout << "reserve()->" << n << endl;char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void clear(){_str[0] = '\0';_size = 0;}//string 修改操作void push_back(char ch){if (_size == _capacity){//2倍扩容reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = ch;++_size;_str[_size] = '\0';}void append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){//至少扩容到_size+lenreserve(_size + len);}strcpy(_str + _size, str);_size += len;}string& operator+=(char ch) {push_back(ch);return *this;}string& operator+=(const char* str){append(str);return *this;}void insert(size_t pos, size_t n, char ch){assert(pos <= _size);if (_size + n > _capacity){//至少扩容到_size+lenreserve(_size + n);}//挪动数据/*	int end = _size;while (end >= (int)pos){_str[end + n] = _str[end];--end;}*/size_t end = _size;while (end >= pos && end != npos){_str[end + 1] = _str[end];--end;}for (size_t i = 0; i < n; i++){_str[pos + 1] = ch;}_size += n;}void insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);if (_size + len > _capacity){//至少扩容到_size+lenreserve(_size + len);}size_t end = _size;while (end >= pos && end != npos){_str[end + len] = _str[end];--end;}for (size_t i = 0; i < len; i++){_str[pos + i] = str[i];}_str += len;}void erase(size_t pos, size_t len = npos){assert(pos <= _size);if (len == npos || pos + len >= _size){_size = pos;_str[_size] = '\0';}else{size_t end = pos + len;while (end <= _size){_str[pos++] = _str[end++];}_size -= len;}}size_t find(char ch, size_t pos = 0){assert(pos < _size);for (size_t i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return npos;}size_t find(const char* str, size_t pos = 0){assert(pos < _size);const char* ptr = strstr(_str + pos, str);if (ptr){return ptr - _str;}else{return npos;}}string substr(size_t pos = 0, size_t len = npos){assert(pos < _size);size_t n = len;if (len == npos || pos + len > _size){n = _size - pos;}string tmp;tmp.reserve(n);for (size_t i = pos; i < pos + n; i++){tmp += _str[i];}return tmp;}//string 非成员函数//bool operator<(const string& s)//{//	size_t i1 = 0;//	size_t i2 = 0;//	while (i1 < _size && i2 < s._size)//	{//		if (_str[i1] < s._str[i2])//		{//			return true;//		}//		else if (_str[i1] > s._str[i2])//		{//			return false;//		}//		else//		{//			++i1;//			++i2;//		}//	}//	/*if (i1 == _size && i2 != s._size)//	{//		return true;//	}//	else//	{//		return false;//	}*///	//return i1 == _size && i2 != s._size;//	return _size < s._size;//}bool operator<(const string& s) const{int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);return ret == 0 ? _size < s._size : ret < 0;}bool operator==(const string& s) const{return _size == s._size&& memcmp(_str, s._str, _size) == 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);}private:size_t _size;size_t _capacity;char* _str;public://const static size_t npos = -1; // 虽然可以这样用,但是不建议const static size_t npos;};const size_t string::npos = -1;ostream& operator<<(ostream& out, const string& s){/*for (size_t i = 0; i < s.size(); i++){out << s[i];}*/for (auto ch : s){out << ch;}return out;}istream& operator>>(istream& in, string& s){s.clear();char ch = in.get();// 处理前缓冲区前面的空格或者换行while (ch == ' ' || ch == '\n'){ch = in.get();}//in >> ch;char buff[128];int i = 0;while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == 127){buff[i] = '\0';s += buff;i = 0;}//in >> ch;ch = in.get();}if (i != 0){buff[i] = '\0';s += buff;}return in;}};

    test.cpp 

    #include"string.h"void test1()
    {aramae::string s1("hello world!!!");//aramae::string s4();//cout << s1.c_str() << endl;//std::string s2;//cout << s2.c_str() << endl;//aramae::string s3;//cout << s3.c_str() << endl;//aramae::string s5(s1);//cout << s5.c_str() << endl;aramae::string s6 = s1;cout << s6.c_str() << endl;}void test2()
    {aramae::string s1("hello world!!!");for (size_t i = 0; i < s1.size(); i++){cout << s1[i] << " ";}cout << endl;const aramae::string s3("aramae");//aramae::string::const_iterator cit = s3.begin();auto cit = s3.begin();while (cit != s3.end()){cit += 1;cout << *cit << " ";++cit;}cout << endl;aramae::string::iterator it = s1.begin();while (it != s1.end()){*it += 1;cout << *it << " ";++it;}cout << endl;for (auto ch : s1){cout << ch << " ";}cout << endl;}void test3()
    {aramae::string s1("hello world");cout << s1.c_str() << endl;s1.push_back(' ');s1.push_back('#');s1.append("hello bit");cout << s1.c_str() << endl;aramae::string s2("hello world");cout << s2.c_str() << endl;s2 += ' ';s2 += '#';s2 += "hello bit";cout << s2.c_str() << endl;
    }void test4()
    {aramae::string s1("helloworld");cout << s1.c_str() << endl;s1.insert(5, 3, '#');cout << s1.c_str() << endl;s1.insert(0, 3, '#');cout << s1.c_str() << endl;aramae::string s2("helloworld");s2.insert(5, "%%%%%");cout << s2.c_str() << endl;
    }void test5()
    {aramae::string s1("helloworld");cout << s1.c_str() << endl;s1.erase(5, 3);cout << s1.c_str() << endl;s1.erase(5, 30);cout << s1.c_str() << endl;s1.erase(2);cout << s1.c_str() << endl;
    }void test6()
    {aramae::string url = "ftp://www.baidu.com/?tn=65081411_1_oem_dg";size_t pos1 = url.find("://");if (pos1 != aramae::string::npos){aramae::string protocol = url.substr(0, pos1);cout << protocol.c_str() << endl;}size_t pos2 = url.find('/', pos1 + 3);if (pos2 != aramae::string::npos){aramae::string domain = url.substr(pos1 + 3, pos2 - (pos1 + 3));aramae::string uri = url.substr(pos2 + 1);cout << domain.c_str() << endl;cout << uri.c_str() << endl;}
    }void test7()
    {aramae::string s("hello world");s.resize(8);cout << s.c_str() << endl;cout << s << endl;s.resize(13, 'x');cout << s.c_str() << endl;cout << s << endl;s.resize(20, 'y');cout << s.c_str() << endl;cout << s << endl;// c的字符数组,以\0为终止算长度// string不看\0,以size为终止算长度aramae::string ss("hello world");ss += '\0';ss += "!!!!!!!!!!";cout << ss.c_str() << endl;cout << ss << endl;aramae::string copy(ss);cout << ss << endl;cout << copy << endl;ss += "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";cout << ss << endl;
    }void test8()
    {aramae::string s;cin >> s;cout << s << endl;cin >> s;cout << s << endl;//char buff[128];//for (size_t i = 0; i < 5; i++)//{//	cin >> buff[i];//}//for (size_t i = 0; i < 5; i++)//{//	cout << buff[i] << endl;//}
    }void test9()
    {//string s1("bb");//string s2("aaa");//cout << (s1 < s2) << endl;aramae::string s1("hello");aramae::string s2("hello");cout << (s1 < s2) << endl;cout << (s1 > s2) << endl;cout << (s1 == s2) << endl << endl;aramae::string s3("hello");aramae::string s4("helloxxx");cout << (s3 < s4) << endl;cout << (s3 > s4) << endl;cout << (s3 == s4) << endl << endl;aramae::string s5("helloxxx");aramae::string s6("hello");cout << (s5 < s6) << endl;cout << (s5 > s6) << endl;cout << (s5 == s6) << endl << endl;
    }void test10()
    {aramae::string s1("hello");aramae::string s2(s1);cout << s1 << endl;cout << s2 << endl;aramae::string s3("xxxxxxxxxxxxx");s1 = s3;cout << s1 << endl;cout << s3 << endl;
    }int main()
    {//test1();//test2();//test3();//test4();//test5();//test5();//test6();//test7();//test8();//test9();test10();return 0;
    }

    2.扩展阅读 

    C++面试中string类的一种正确写法 | 酷 壳 - CoolShellhttps://coolshell.cn/articles/10478.htmlSTL 的string类怎么啦?_stl 字符串-CSDN博客文章浏览阅读4.5w次,点赞50次,收藏60次。 STL 的string类怎么啦?陈皓 前言 上个周末在和我的同学爬香山闲聊时,同学说到STL中的string类曾经让他备受折磨,几年前他开发一个系统前对string类还比较清楚,然后随着程序的复杂度的加深,到了后期,他几乎对string类失去了信心和信任,他觉得他对string类一头雾水。老实说,我几年前也有同样的痛苦(就是当我写下《标准C++类string的Copy-O_stl 字符串 https://blog.csdn.net/haoel/article/details/1491219


    结语:感谢相遇

    /// 高山仰止,景行行止。虽不能至,心向往之 ///

    http://www.lryc.cn/news/582124.html

    相关文章:

  • Day07- 管理并发和并行挑战:竞争条件和死锁
  • 【AI大模型入门指南】机器学习入门详解
  • 烟雾,火焰探测器
  • Linux操作系统:软硬链接与动静态库
  • ClickHouse介绍与应用
  • 迁移GitLab,在新Linux中用Docker重新部署GitLab备份还原
  • C#中的BindingList有什么作用?
  • 【机器学习深度学习】多分类评估策略:混淆矩阵计算场景模拟示例
  • 亚马逊运营进阶指南:如何用AI工具赋能广告运营
  • 诊断工程师进阶篇 --- 车载诊断怎么与时俱进?
  • English Practice - Day 2
  • vite打包的简单配置
  • react状态管理库 - zustand
  • 风电自动化发电中的通信桥梁:CAN主站转MODBUS TCP网关解析
  • 【MyBatis】MyBatis与Spring和Spring Boot整合原理
  • 5种方法将联系人从iPhone转移到OnePlus
  • C++--map和set的使用
  • 仿mudou库one thread oneloop式并发服务器
  • 达梦数据库的信息查询
  • Redisson 分布式锁原理解析
  • Navicat Premium可视化工具使用查询控制台优化SQL语句
  • 商品中心—库存分桶高并发的优化文档
  • 力扣 3258 统计满足 K 约束的子字符串数量 I 题解
  • Java工具类,对象List提取某个属性为List,对象List转为对象Map其中某个属性作为Key值
  • RAG实战指南 Day 8:PDF、Word和HTML文档解析实战
  • UI自动化常见面试题
  • day08-Elasticsearch
  • 云计算领域“XaaS”是什么?
  • Python编译器(Pycharm Jupyter)
  • 第4.2节 Android App生成追溯关系