模拟实现string
模拟实现string只是仿造库里的功能模拟实现,并不和库中代码一样。需要知道的是,string在不同的编译器下,string的实现也是有差异的。
#pragma once
#include <assert.h>
namespace QiBL
{class string{public:typedef char* iterator;//将iterator写在类里面,这样每一个类都有自己的iterator,所以使用的时候要指定是哪个类的迭代器,迭代器像指针但是可能不是指针typedef const char* const_iterator;iterator begin()//begin指向头{return _str;}iterator end()//end指向尾的下一个,即"\0"{return _str + _size;}const_iterator begin() const//begin指向头{return _str;}const_iterator end() const//end指向尾的下一个,即"\0"{return _str + _size;}public:string(const char* str = "")//空串自动保留一个"\0",不能使用nullptr,不然在c_str函数返回会报错//:_str(new char[1])//开一个空间存"\0"标识符//, _size(0)//_size表示存了多少数据 不算"\0"//, _capacity(0)//_capacity表示能存多少数据 不算"\0": _size(strlen(str)){_capacity = _size;//最开始_capacity与_size大小相同,都是初始化的大小,不包含"\0"_str = new char[_capacity + 1];//strlen计算字符串大小的时候不算"\0",所以开空间的时候多加了1strcpy(_str, str);}/*string(const string& s){_str = new char[s._capacity + 1];_size = s._size;_capacity = s._capacity;strcpy(_str, s._str);}*/string(const string& s){string tmp(s._str);swap(tmp);}char* c_str() const{return _str;}size_t size() const//加const,保证const对象也能调用{return _size;}size_t capacity() const//加const,保证const对象也能调用{return _capacity;}char& operator[](size_t i)//为了可读可写,所以使用引用返回,因为在堆上,所以函数结束数据还在{assert(i < _size);return _str[i];}const char& operator[](size_t i) const//只读,加const,保证const对象也能调用{//前面加const是为了返回值是引用,会被其他引用改变返回位置的值assert(i < _size);return _str[i];}void reserve(size_t n)//扩容 {if (n > _capacity)//n比_capacity大才扩容,否则不变,不能缩容{char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void push_back(char c)//尾插一个字符{if (_size == _capacity)reserve(_capacity == 0 ? 4 : _capacity * 2);_str[_size] = c;++_size;_str[_size] = '\0';}void append(const char* str)//尾插一个字符串{size_t len = strlen(str);if ((_size + len) > _capacity){reserve(_size + len);}strcpy(_str + _size, str);//strcpy会将'\0',也拷贝过来_size += len;}string& operator+=(char c){push_back(c);return *this;}string& operator+=(const char* str){append(str);return *this;}/*string& operator=(const string& s){char* tmp = new char[s._capacity + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;_size = s._size;_capacity = s._capacity;return *this;}*/string& operator=(string s){swap(s);return *this;}void insert(size_t pos, char c)//在pos前插入字符c,下表从0开始,0是头插,等于_size就是尾插{assert(pos <= _size);if (_size == _capacity)reserve(_capacity == 0 ? 4 : _capacity * 2);size_t end = _size + 1;//end指向'\0'的下一个while (pos < end){_str[end] = _str[end - 1];--end;}_str[pos] = c;++_size;}void insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);if ((_size + len) > _capacity)reserve(_size + len);size_t end = _size + len;//while (end - len > pos - 1)//pos可能是0,所以要写为加while (end > pos + len - 1){_str[end] = _str[end - len];--end;}strncpy(_str + pos, str, len);_size = _size + len;}void erase(size_t pos = 0, size_t len = npos)//从pos位置删除len个{assert(pos < _size);//位置是 0 - size - 1//if (len == npos || pos + len >= _size)//这样写有BUG,有溢出的风险,假设len = nops - 1if (len == npos || len >= _size - pos)//这种写法可以规避掉风险{_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);_size = _size - len;}}void resize(size_t n, char c = '\0')//改变有效数据的大小,但是不缩容{if (n <= _size){_str[n] = '\0';_size = n;}else{reserve(n);for (size_t i = _size; i < n; i++){_str[i] = c;} _size = n;_str[_size] = '\0';}}void swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}size_t find(char ch, size_t pos = 0)//指定位置开始,找字符返回下标,找不到就返回npos{assert(pos < _size);for (size_t i = pos; i < _size; i++){if (_str[i] == ch)return i;}return npos;}size_t find(const char* sub, size_t pos = 0)//指定位置开始,找字符串返回下标,找不到就返回npos{assert(pos < _size);char* p = strstr(_str + pos, sub);//strstr是C语言库中匹配字符串的库函数if (p){return p - _str;}return npos;}string substr(size_t pos = 0, size_t n = npos)//指定位置取长度为n的字符串{assert(pos < _size);string s;if (n >= _size - pos)//从下标0开始{for (size_t i = pos; i < _size; i++){s += _str[i];}}else{for (size_t i = pos; i < pos + n; i++){s += _str[i];}}return s;}void clear(){_str[0] = '\0';_size = 0;}~string(){delete[] _str;_str = nullptr;_size = 0;_capacity = 0;} private:char* _str = nullptr;size_t _size = 0;size_t _capacity = 0;public:static const int npos;};bool operator==(const string& s1, const string& s2){int ret = strcmp(s1.c_str(), s2.c_str());return ret == 0;}bool operator<(const string& s1, const string& s2){int ret = strcmp(s1.c_str(), s2.c_str());//s1小于s2返回-1return ret < 0;}bool operator<=(const string& s1, const string& s2){if (s1 < s2 || s1 == s2)return true;elsereturn false;}bool operator>(const string& s1, const string& s2){if (s1 <= s2)return false;elsereturn true;}bool operator>=(const string& s1, const string& s2){if (s1 < s2)return false;elsereturn true;}ostream& operator<<(ostream& out, const string& s){for (auto s : s){out << s;}return out;}istream& operator>>(istream& in, string& s){s.clear();char ch;//in >> ch;//C++的cin和C语言的scanf不支持提取空格和换行ch = in.get();//推荐使用istream类的get(),类似get char(),但是更好while (ch != ' ' && ch != '\n'){s += ch;ch = in.get();}return in;}istream& getline(istream& in, string& s)//一次输入一行内容{s.clear();char ch;//in >> ch;//C++的cin和C语言的scanf不支持提取空格和换行ch = in.get();//推荐使用istream类的get(),类似get char(),但是更好while (ch != '\n'){s += ch;ch = in.get();}return in;}const int string::npos = -1;void Stringtest1(){string s1 = "Hello World";for (int i = 0; i < s1.size(); i++){cout << s1[i] << " ";}cout << endl;s1[1] = 'W';for (int i = 0; i < s1.size(); i++){cout << s1[i] << " ";}cout << endl;string s2 = "Wello World";for (int i = 0; i < s2.size(); i++){cout << s2[i] << " ";}cout << endl;cout << "Stringtest1()" << endl;}void Stringtest2(){string s3 = "Hello World";string::iterator i3 = s3.begin();while (i3 != s3.end()){(*i3)++;cout << *i3 << " ";i3++;}cout << endl;for (auto i : s3)//经尝试,范围for只识别begin和end,当iterator换成其他宏,只要begin和end在就能使用,且begin和end的大小写不能换{//范围for底层是一个死板的替换,并不神秘i--;cout << i << " ";}cout << endl;cout << "Stringtest2()" << endl;}void Stringtest3(){const string s4 = "Hello World";string::const_iterator i4 = s4.begin();while (i4 != s4.end()){//(*i4)++;cout << *i4 << " ";i4++;}cout << endl;//虽然s4被const修饰,但是范围for得到的是s4的拷贝,所以修改的也是临时变量的值,所以不会报错/*for (auto& i : s4){i--;cout << i << " ";}cout << endl;*///for (auto& i : s4)//范围for使用引用就不是拷贝了{//综上可以看出,范围for是相当于函数传参一般//i--;cout << i << " ";}cout << endl;cout << "Stringtest3()" << endl;}void Stringtest4(){string s4 = "Hello World";s4.push_back('H');cout << s4.c_str();cout << endl;cout << "Stringtest4()" << endl;}void Stringtest5(){string s4 = "Hello World";s4 += "XYZ";s4 += "QBL";s4 += "Q";cout << s4.c_str()<< endl;cout << "Stringtest5()" << endl;}void Stringtest6(){string s4 = "Hello World";s4.insert(11, 'Q');cout << s4.c_str() << endl;cout << "Stringtest6()" << endl;}void Stringtest7(){string s4 = "Hello World";s4.insert(0, "QQQ");cout << s4.c_str() << endl;cout << "Stringtest7()" << endl;}void Stringtest8(){string s4 = "Hello World";s4.erase(10);cout << s4.c_str() << endl;cout << "Stringtest8()" << endl;}void Stringtest9(){string s4 = "Hello World";s4.resize(5);cout << s4.c_str() << endl;s4.resize(10, 'x');cout << s4.c_str() << endl;cout << "Stringtest9()" << endl;}void Stringtest10(){string s4 = "Hello World";string s5 = s4;cout << s4.c_str() << endl;s5.erase(5, 3);cout << s5.c_str() << endl;cout << "Stringtest10()" << endl;}void Stringtest11(){string s4 = "Hello World";string s5 = s4.substr(10);cout << s5.c_str() << endl;cout << "Stringtest11()" << endl;}void Stringtest12(){string s4 = "Hello World";string s5 = "Hello World";cout << (s5 == s4) << endl;cout << "Stringtest12()" << endl;}void Stringtest13(){string s4 = "Hello World";string s5 = "Hello World1";cout << (s5 == s4) << endl;cout << (s5 > s4) << endl;cout << (s5 < s4) << endl;cout << (s5 >= s4) << endl;cout << (s5 <= s4) << endl;cout << "Stringtest13()" << endl;}void Stringtest14(){string s4 = "Hello World";cout << s4 << endl;cout << "Stringtest14()" << endl;}void Stringtest15(){string s4 = "Hello World";cout << s4 << endl;string s5 = "Hello World";cout << s4 << endl;cin >> s4 >> s5;cout << s4 << endl;cout << s5 << endl;cout << "Stringtest15()" << endl;}void Stringtest16(){string s4 = "Hello World";cout << s4 << endl;getline(cin, s4);cout << s4 << endl;cout << "Stringtest16()" << endl;}
}