介绍:
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类的大小进行输出字符即可,但输入流需注意以下两点:
在进行对string输入时,若里面有数据,需将其清空。
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); }
【c++】string类的模拟实现--2 https://developer.aliyun.com/article/1424722?spm=a2c6h.13148508.setting.24.214f4f0elA5UEs