string类的使用和模拟实现

string类的使用和模拟实现 1.标准库中的string类在了解库中的string类的使用之前我们先了解两个小的语法 auto关键字和范围for.1.1auto关键字和范围for在早期c/C中auto的含义是使用auto修饰的变量是具有自动存储器的局部变量后来这个不重要了。C11中标准委员会变废为宝赋予了auto全新的含义即auto不再是一个存储类型指示符而是作为一个新的类型指示符来指示编译器auto声明的变量必须由编译器在编译时期推导而得。用auto声明指针类型时用auto和auto*没有任何区别但用auto声明引用类型时则必须加当在同一行声明多个变量时这些变量必须是相同的类型否则编译器将会报错因为编译器实际只对第一个类型进行推导然后用推导出来的类型定义其他变量。auto不能作为函数的参数可以做返回值但是建议谨慎使用auto不能直接用来声明数组// 不能做参数 void func2(auto a) { } // 可以做返回值但是建议谨慎使用 auto func3() { return 3; } void test1() { int a 12; auto b a; auto c a; // error C3531: “d”: 类型包含“auto”的符号必须具有初始值设定项 //auto d; cout typeid(b).name() endl; cout typeid(c).name() endl; int x 10; auto y x; auto z x;//指针类型 auto m x;//引用 cout typeid(x).name() endl; cout typeid(y).name() endl; cout typeid(z).name() endl; cout typeid(m).name() endl; auto aa 1, bb 2; //error C3538: 在声明符列表中“auto”必须始终推导为同一类型 auto cc 3, dd 4.0; //“auto []”: 数组不能具有其中包含“auto”的元素类型 auto array[] { 4, 5, 6 }; }范围for对于一个有范围的集合而言由程序员来说明循环的范围是多余的有时候还会容易犯错误。因此C11中引入了基于范围的for循环。for循环后的括号由冒号”“分为两部分第一部分是范围内用于迭代的变量第二部分则表示被迭代的范围自动迭代自动取数据自动判断结束。范围for可以作用到数组和容器对象上进行遍历范围for的底层很简单容器遍历实际就是替换为迭代器这个从汇编层也可以看到。void test2() { int arry[] { 1,2,3,4,5,6,7,8,9 }; for (auto e : arry)//auto使用场景之一 { cout e ; } cout endl; for (auto e : arry) { e; cout e ; } cout endl; }1.2string类的常用接口说明下面主要介绍一些常用的接口具体的所有接口可查官方文档cplusplus.com/reference/string类中常见的构造主要介绍前六个的使用赋值运算符重载析构和我们普通类的使用一样。string类对象的访问及遍历操作1.迭代器遍历访问begin(、end(返回正向迭代器实现正向遍历。begin(指向首字符end(是尾后迭代器指向末尾字符的下一个位置。rbegin(、end(返回反向迭代器实现逆序遍历。rbegin(指向末尾字符rend(指向首字符的前一位置。上面4个迭代器都重载了const版本编译器自动调用最符合的版本string s hello world; string::iterator it s.begin(); while (it ! s.end()) { cout (*it) ; it; } cout endl; string::reverse_iterator rit s.rbegin(); while (rit ! s.rend()) { cout (*rit) ; rit; } cout endl; const string s1 hello world; string::const_iterator cit s1.begin(); while (cit! s1.end()) { cout (*cit) ; cit; } cout endl; string::const_reverse_iterator rcit s1.rbegin(); while (rcit ! s1.rend()) { cout (*rcit) ; rcit; }注意在打印迭代器指向的内容要加上*其次反向迭代器遍历的时候迭代器也是不是--。2.下标访问cout s1 endl; cout s1[2] endl; cout s1.at(3) endl; cout s1.back() endl; cout s1.front() endl;string类对象的容量操作string s1(hello world); cout s1.size() endl; cout s1.length() endl; cout s1.max_size() endl; cout s1.capacity() endl; //验证vs如何扩容 /*int c s1.capacity(); for (int i 0;i 100;i) { s1.push_back(x); if (s1.capacity() ! c) { c s1.capacity(); cout s1.capacity() endl; } }*/ cout s1 s1.size(::s1.size() s1.capacity()s1.capacity()endl; s1.resize(2);//比size 小 cout s1 s1.size(:: s1.size() s1.capacity() s1.capacity() endl; s1.resize(29, x);//比size 大 cout s1 s1.size(:: s1.size() s1.capacity() s1.capacity() endl; cout s1 s1.size(:: s1.size() s1.capacity() s1.capacity() endl; s1.reserve(50); cout s1 s1.size(:: s1.size() s1.capacity() s1.capacity() endl; cout s1 s1.size(:: s1.size() s1.capacity() s1.capacity() endl; s1.shrink_to_fit(); cout s1 s1.size(:: s1.size() s1.capacity() s1.capacity() endl; cout s1 s1.size(:: s1.size() s1.capacity() s1.capacity() endl; s1.clear(); cout s1 s1.size(:: s1.size() s1.capacity() s1.capacity() endl; cout s1.empty() endl;注意1.size0与length0方法底层实现原理完全相同引l入size0的原因是为了与其他容器的接口保持一致一般情况下基本都是用size0。2.clearo只是将string中有效字符清空不改变底层空间大小。3.resize(sizetn与resize(size_tncharc都是将字符串中有效字符个数改变到n个不同的是当字符个数增多时resize(n用o来填充多出的元素空间resizesize_tnchar c用字符c来填充多出的元素空间。注意resize在改变元素个数时如果是将元素个数增多可能会改变底层容量的大小如果是将元素个数减少底层空间总大小不变。4.reserve(size_tres_arg0)为string预留空间不改变有效元素个数当reserve的参数小string的底层空间总大小时reserver不会改变容量大小。 5.shrink_to_fit()请求缩减string的底层容量使其匹配当前有效字符个数size。该操作仅为请求标准不强制实现必定收缩执行后不会改变字符串有效元素数量string类对象的部分修改操作注意上面部分操作的函数原型只给出部分1.在string尾部追加字符时s.push_back(c)/s.append(1c)/sc三种的实现方式差不多一般情况下string类的操作用的比较多操作不仅可以连接单个字符还可以连接字符串。2.对string操作时如果能够大概预估到放多少字符可以先通过reserve把空间预留好。void test6() { string s1 hello; string s2 world; s1 ; s1 s2; cout s1 endl; s1.append(s2); cout s1 endl; s1.append(s2, 0, 3); cout s1 endl; s1.append(5,h); cout s1 endl; s1.assign(s2); cout s1 endl; s1.insert(0, s2); cout s1 endl; s1.erase(3, 2); cout s1 endl; s1.pop_back(); cout s1 endl; }string类对象的字符串操作上面部分操作的函数原型只给出部分void test7() { //string s1(hello world); //char* str new char[s1.size()]; //strcpy(str, s1.c_str()); //strcpy(str, s1.data()); //cout str endl; string str(There are two needles in this haystack with needles.); string str2(needle); size_t found str.find(str2);//14 if (found ! string::npos) cout first needle found at: found \n; found str.find(needles are small, found 1, 6); if (found ! string::npos) cout second needle found at: found \n; found str.find(haystack); if (found ! string::npos) cout haystack also found at: found \n; found str.find(.); if (found ! std::string::npos) cout Period found at: found \n; str.replace(str.find(str2), str2.length(), preposition); cout str \n; size_t rfound str.rfind(str2); if (rfound ! string::npos) cout first needle found at: rfound \n; rfound str.rfind(needles are small, rfound - 1, 6); if (rfound ! string::npos) cout second needle found at: rfound \n; rfound str.find(haystack); if (rfound ! string::npos) cout haystack also found at: rfound \n; rfound str.rfind(.); if (rfound ! std::string::npos) cout Period found at: rfound \n; string str(Please, replace the vowels in this sentence by asterisks.); //size_t found str.find_first_of(aeiou); //size_t found str.find_last_of(aeiou); //size_t found str.find_first_not_of(aeiou); size_t found str.find_last_not_of(aeiou); while (found ! string::npos) { str[found] *; if (found 0) { found str.find_last_not_of(aeiou, found - 1); } else { // 已经到下标0前面没有字符了直接终止 found string::npos; } } cout str \n; string s1(asdfghjkl); string s2 s1.substr(2, 5); cout s1 endl; cout s2 endl; }string类的非成员函数2.string类的模拟实现下面模拟实现我们只实现部分常用接口主要是为了更好了解底层的string类。首先我们定义一个string类私有的成员变量有三个存储字符串的指针_str字符串大小_size还有字符串数组空间的大小_capacity这里的_size和_capacity都是不包含\0的大小。接下来就是模拟实现函数接口的定义string.h。#includestring.h #includeassert.h namespace gxy { class string { public: typedef char* iterator; iterator begin(); iterator end(); string(); string(const char* s); ~string(); const char* c_str() const; size_t size() const; char operator[] (size_t i); const char operator[] (size_t i) const; private: char* _str; size_t _size; size_t _capacity; }; }阶段一模拟实现构造函数析构函数和遍历字符串构造函数我们主要实现无参构造和字符串构造在无参构造中并不是直接把_str初始化为nullptr,而是开辟一个空间存放\0,这样的好处有统一内部函数接口避免到处判空防止调用c字符串库strlen,strcpy崩溃。采用new [],是为了在析构是统一调用delete[]。字符串构造就是要开辟一个一样大小的空间然后把字符串拷贝到_str就可以然后初始化_size和_capacity,看左边的实现代码发现调用了三次strlen而strlen的实现是遍历字符串一个一个拷贝效率就很低再这个基础上我们就调用一次strlen给_size,然后用_size来初始化注意这里不能再初始化列表先初始化_size,这样子定义的成员变量的顺序也要修改因为初始化列表的初始化顺序和定义的成员变量的顺序一样但是可以再函数体里初始化即右边代码。析构函数释放资源空间前面开空间统一调用new[],释放直接调用delete [],_size0,_capacity0。为了打印出对印的字符串先实现一个c_str,转换成char *类型打印后面再实现流提取因_str的类型就是char *直接放回_str即可这里只是打印字符串不需要修改定义成const 成员函数上面右图。遍历打印字符串有下标访问范围for和迭代器。下标访问要重载[] 操作符和size函数接口[]操作符重载实现访问一个字符可以通过指针模拟实现字符串数组名就是首地址首地址加i就可以访问指定下标i的元素实现的时候断言一下i 是否合法,考虑到字符对象可能为const,所以我们实现两个版本size 函数接口直接返回_size。char string::operator[] (size_t i) { assert(i _size); return *(_str i); } const char string::operator[] (size_t i) const { assert(i _size); return *(_str i); } size_t string::size() const { return _size; }范围for底层也是通过迭代器访问迭代器我们这里可以先简单理解成和指针类似实现我们也采用指针的方式begin() 反回第一个字符指针end()返回最后一个字符的下一个位置的指针这里统样实现两个版本。string::iterator string::begin() { return _str; } string::iterator string::end() { return _str_size; } string::const_iterator string::begin() const { return _str; } string::const_iterator string::end() const { return _str _size; }阶段二主要实现字符串的相关操作在实现插入接口前先实现一个扩容的操作这里可以借助函数void reserve(size_t n)当n_capacity先开辟一个n1大小的字符数组tmp1是保存\0,在将_str拷贝到tmp,释放_str,将_str指向tmp,修改——capacity,小于n 这里不做处理。void string::reserve(size_t n) { if (n _capacity) { char* tem new char[n 1]; memcpy(tem, _str, _size1); delete[] _str; _str tem; _capacity n; } }接着实现尾插的操作尾插一个字符void push_back(char ch)和尾插一个字符串void append(const char* str)两个函数接口的实现逻辑基本一样首先考虑扩容尾插一个字符直接在下标_size处添加ch然后_size,尾插一个字符串借用memcpy()来实现最后注意不要忘记\0.void string::push_back(char ch) { if (_size _capacity) { //扩容 size_t newcapacity _capacity 0 ? 4 : 2 * _capacity; reserve(newcapacity); } _str[_size] ch; _size; _str[_size] \0; } void string::append(const char* str) { assert(str); size_t len strlen(str); if (_sizelen _capacity) { //扩容 size_t newcapacity 2 * _capacity _size len ? 2 * _capacity : _size len; reserve(newcapacity); } memcpy(_str _size, str, len1); _size len; }有了上面两个函数接口尾插一个字符和尾插一个字符串实现运算符重载就可以直接复用string string::operator(char ch) { push_back(ch); return *this; } string string::operator(const char* str) { append(str); return *this; }有了尾插后还可以在指定下标前插入字符string insert(size_t pos, char ch)和插入一个字符串string insert(size_t pos, const char* str)同样的插入前先判断需不需要扩容因可以在字符串的任意下标位置插入就要从后往前挪动字符插入一个字符在pos位置整体往后挪动一个位置插入一个字符串整体往后挪动strlen(str)个位置,在插入字符和字符串最后修改_size。注意挪动字符时控制循环要注意下标是size_t 类型string string::insert(size_t pos, char ch) { assert(pos _size); if (_size _capacity) { //扩容 size_t newcapacity _capacity 0 ? 4 : 2 * _capacity; reserve(newcapacity); } //挪动数据 //int end _size; //while (end (int)pos) //{ // _str[end 1] _str[end]; // end--; //} size_t end _size 1; while (end pos) { _str[end] _str[end - 1]; end--; } _str[pos] ch; _size; return *this; } string string::insert(size_t pos, const char* str) { assert(pos _size); size_t len strlen(str); if (_size len _capacity) { //扩容 size_t newcapacity 2 * _capacity _size len ? 2 * _capacity : _size len; reserve(newcapacity); } //挪动数据 size_t end _size len; while (end pos len-1) { _str[end] _str[end - len]; end--; } for (size_t i 0; i len; i) { _str[pos i] str[i]; } _size len; return *this; }删除操作实现尾删一个字符void pop_back()和指定下标和长度删除字符串string erase(size_t pos 0, size_t len npos)尾删一个字符修改_size就可以完成erase接口的实现可以分成两种情况如果lennpos或者删除的长度大于当前下标到结尾有效的字符个数直接修改_size,否则就要从后往前挪动字符借助memmove()来实现。注意要考虑到\0void string::pop_back() { assert(_size 0); _str[--_size] \0; } string string::erase(size_t pos, size_t len) { assert(pos _size); if (lenstring::npos||len _size - pos) { _size pos; _str[pos] \0; } else { size_t i pos len; //while (i _size) //{ // _str[i - len] _str[i]; // i; //} memmove(_str pos, _str i, _size-i1); _size - len; } return *this; }查找接口模拟实现查找一个字符返回第一个下标size_t find(char ch, size_t pos 0) const和查找子串返回匹配的第一个下标size_t find(const char* str, size_t pos 0) const查找一个字符遍历一边字符串找到就直接返回否则返回npos,查找子串借助strstr()查找返回匹配的第一个字符的地址如果找到用该地址减去首地址就是下标否则返回npos.size_t string::find(char ch, size_t pos) const { assert(pos _size); for (size_t i pos;i _size;i) { if (_str[i] ch) return i; } return npos; } size_t string::find(const char* str, size_t pos ) const { assert(pos _size); const char* p strstr(_str pos, str); if (p nullptr) { return npos; } else { return p - _str; } }有了查找的接口再模拟实现分割子串的函数接口string substr(size_t pos, size_t len npos) const可以创建一个空的ret 对象然后将子串添加到ret对象再这之前要先确定len的长度如果lennpos或者删除的长度大于当前下标到结尾有效的字符个数修改len _size - pos这里可以直接复用前面的运算符将字符串加到ret对象最后返回对象ret。string string::substr(size_t pos, size_t len) const { assert(pos _size); if (len npos || _size - pos len) { len _size - pos; } string ret; for (size_t i pos;i len;i) { ret _str[i]; } return ret; }最后就是对象的六个比较运算符的重载实现这里实现了和,其他接口复用这两个接口就可以完成比较运算符的重载就是比较底层的字符串的字符的ascll值这里的逻辑还是比较简单直接看代码。bool string::operator(const string s) const { size_t s1 0, s2 0; while (s1 _size s2 s._size) { if (_str[s1] s._str[s2]) { s1; s2; } if (_str[s1] s._str[s2]) { return true; } if (_str[s1] s._str[s2]) { return false; } } return s2 s._size; } bool string::operator(const string s) const { return *this s || *this s; } bool string::operator(const string s) const { return !(*this s); } bool string::operator(const string s) const { return !(*this s); } bool string::operator(const string s) const { size_t s1 0, s2 0; while (s1 _size s2 s._size) { if (_str[s1] ! s._str[s2]) { return false; } s1; s2; } return s1 _size s2s._size; } bool string::operator!(const string s) const { return !(*this s); }阶段三补充拷贝构造和赋值重载和模拟实现非成员函数拷贝构造和赋值运算符重载这里都有资源的申请所以浅拷贝就无法完成就要自己实现一个深拷贝先开辟一个一样大小的空间然后借助memcpy把字符串内容拷贝过去在初始化_size和_capacity,赋值运算符逻辑是一样就是先借助一个临时数组来存储要拷贝的字符串然后释放当前的字符串再指向这个临时的字符串。string::string(const string s) { _str new char[s._capacity 1]; memcpy(_str, s._str, s._size 1); _size s._size; _capacity s._capacity; } string string::operator(const string s) { if (this ! s) { char *tmp new char[s._capacity 1]; memcpy(tmp, s._str, s._size 1); delete[] _str; _str tmp; _size s._size; _capacity s._capacity; } return *this; }在上面程序中发现交换数据可以封装成一个成员函数void swap(string s),交换对象的变量就调用库里的swapvoid string::swap(string s) { std::swap(_str, s._str); std::swap(_size, s._size); std::swap(_capacity, s._capacity); }使用 swap 实现现代拷贝构造与赋值重载思路是先构造源对象副本再与当前对象交换资源。需要特别区分拷贝构造和赋值重载拷贝构造中this指向的对象尚未完成构造成员变量是栈随机垃圾值如果直接 swap临时对象析构会释放随机野指针造成未定义行为。因此拷贝构造要在初始化列表中将当前对象初始化为合法空对象之后再 swap。而传值形式的赋值重载中*this已经是完整对象传入的形参也是已经构造完成的副本可以直接 swap无需额外初始化。这里实现是给成员变量缺省值//现代写法 string::string(const string s) { string tmp(s._str); swap(tmp); } string string::operator(string s) { swap(s); return *this; }分成员函数主要实现流插入、流提取、和swap。函数ostream operator(ostream out, const string s)的实现用一个循环控制一个字符一个字符打印避免遇到\0就停止打印。ostream operator(ostream out, const string s) { // out s.c_str() endl;//遇到\0就停止打印 for (size_t i 0; i s.size(); i) { out s[i]; } return out; }operator流提取重载实现自定义string的输入功能。读取输入流字符空格、换行作为读取结束标志。采用大小为 128 的局部缓冲区分段接收字符防止栈缓冲区溢出。缓冲区存满时就把数据追加到 string 对象清空缓冲区继续读取读取结束后把缓冲区剩余内容追加给 string。返回 istream 引用支持链式输入。调用s.clear()先清空原有内容把clear()封装一个成员函数实现也比较简单功能是清除字符串修改_size 就可以完成。void string::clear() { _str[0] \0; _size 0; } istream operator(istream in, string s) { s.clear(); char buff[128]; int i 0; char ch in.get(); while (ch ! ch ! \n) { buff[i] ch; if (i 127) { buff[i] \0; s buff; i 0; } ch in.get(); } if (i 0) { buff[i] \0; s buff; } return in; }有了上面的流提取函数实现逻辑函数istream getline(istream is, string str, char delim \n);的实现就简单只要改变结束字符就可以。istream getline(istream is, string str, char delim) { str.clear(); char buff[128]; int i 0; char ch is.get(); while (ch ! delim) { buff[i] ch; if (i 127) { buff[i] \0; str buff; i 0; } ch is.get(); } if (i 0) { buff[i] \0; str buff; } return is; }最后一个非成员函数void swap(string x, string y),交换两个对象就可以借助成员函数的swap实现。void swap(string x, string y) { x.swap(y); }以上就是我个人学习的string类使用后简单的模拟实现的string类。模拟实现源代码string.h#pragma once #includeiostream #includestring.h #includeassert.h #includealgorithm using namespace std; namespace gxy { class string { public: typedef char* iterator; typedef const char* const_iterator; iterator begin(); iterator end(); const_iterator begin() const; const_iterator end() const; //string(); string(const char* s); ~string(); string(const string s); //string operator(const string s); string operator(string s); void swap(string s); const char* c_str() const; size_t size() const; char operator[] (size_t i); const char operator[] (size_t i) const; void reserve(size_t n); void push_back(char ch); void append(const char* str); string operator(char ch); string operator(const char* str); void pop_back(); string insert(size_t pos, char ch); string insert(size_t pos, const char* str); string erase(size_t pos 0, size_t len npos); size_t find(char ch, size_t pos 0) const; size_t find(const char* str, size_t pos 0) const; string substr(size_t pos, size_t len npos) const; void clear(); bool operator(const string s) const; bool operator(const string s) const; bool operator(const string s) const; bool operator(const string s) const; bool operator(const string s) const; bool operator!(const string s) const; private: char* _str nullptr; size_t _size0; size_t _capacity0; public: const static size_t npos; }; ostream operator(ostream out, const string s); istream operator(istream in, string s); istream getline(istream is, string str, char delim \n); void swap(string x, string y); }string.cpp#define _CRT_SECURE_NO_WARNINGS #includeString.h namespace gxy { const size_t string::npos -1; // string::string() // :_str(new char [1] {\0}) // ,_size ( 0) // ,_capacity (0) //{} //string::string(const char* s) // :_str(new char[strlen(s) 1]) // ,_size(strlen(s)) // ,_capacity(strlen(s)) //{ //} string::string(const char* s) :_size (strlen(s)) { _str new char[_size 1]; strcpy(_str, s); _capacity _size; } string::~string() { delete[] _str; _size 0; _capacity 0; } const char* string::c_str() const { return _str; } size_t string::size() const { return _size; } string::iterator string::begin() { return _str; } string::iterator string::end() { return _str_size; } string::const_iterator string::begin() const { return _str; } string::const_iterator string::end() const { return _str _size; } char string::operator[] (size_t i) { assert(i _size); return *(_str i); } const char string::operator[] (size_t i) const { assert(i _size); return *(_str i); } void string::reserve(size_t n) { if (n _capacity) { char* tem new char[n 1]; memcpy(tem, _str, _size1); delete[] _str; _str tem; _capacity n; } } void string::push_back(char ch) { if (_size _capacity) { //扩容 size_t newcapacity _capacity 0 ? 4 : 2 * _capacity; reserve(newcapacity); } _str[_size] ch; _size; _str[_size] \0; } void string::append(const char* str) { assert(str); size_t len strlen(str); if (_sizelen _capacity) { //扩容 size_t newcapacity 2 * _capacity _size len ? 2 * _capacity : _size len; reserve(newcapacity); } memcpy(_str _size, str, len1); _size len; } string string::operator(char ch) { push_back(ch); return *this; } string string::operator(const char* str) { append(str); return *this; } void string::pop_back() { assert(_size 0); _str[--_size] \0; } string string::insert(size_t pos, char ch) { assert(pos _size); if (_size _capacity) { //扩容 size_t newcapacity _capacity 0 ? 4 : 2 * _capacity; reserve(newcapacity); } //挪动数据 //int end _size; //while (end (int)pos) //{ // _str[end 1] _str[end]; // end--; //} size_t end _size 1; while (end pos) { _str[end] _str[end - 1]; end--; } _str[pos] ch; _size; return *this; } string string::insert(size_t pos, const char* str) { assert(pos _size); size_t len strlen(str); if (_size len _capacity) { //扩容 size_t newcapacity 2 * _capacity _size len ? 2 * _capacity : _size len; reserve(newcapacity); } //挪动数据 size_t end _size len; while (end pos len-1) { _str[end] _str[end - len]; end--; } for (size_t i 0; i len; i) { _str[pos i] str[i]; } _size len; return *this; } string string::erase(size_t pos, size_t len) { assert(pos _size); if (lenstring::npos||len _size - pos) { _size pos; _str[pos] \0; } else { size_t i pos len; //while (i _size) //{ // _str[i - len] _str[i]; // i; //} memmove(_str pos, _str i, _size-i1); _size - len; } return *this; } size_t string::find(char ch, size_t pos) const { assert(pos _size); for (size_t i pos;i _size;i) { if (_str[i] ch) return i; } return npos; } size_t string::find(const char* str, size_t pos ) const { assert(pos _size); const char* p strstr(_str pos, str); if (p nullptr) { return npos; } else { return p - _str; } } string string::substr(size_t pos, size_t len) const { assert(pos _size); if (len npos || _size - pos len) { len _size - pos; } string ret; for (size_t i pos;i len;i) { ret _str[i]; } return ret; } bool string::operator(const string s) const { size_t s1 0, s2 0; while (s1 _size s2 s._size) { if (_str[s1] s._str[s2]) { s1; s2; } if (_str[s1] s._str[s2]) { return true; } if (_str[s1] s._str[s2]) { return false; } } return s2 s._size; } bool string::operator(const string s) const { return *this s || *this s; } bool string::operator(const string s) const { return !(*this s); } bool string::operator(const string s) const { return !(*this s); } bool string::operator(const string s) const { size_t s1 0, s2 0; while (s1 _size s2 s._size) { if (_str[s1] ! s._str[s2]) { return false; } s1; s2; } return s1 _size s2s._size; } bool string::operator!(const string s) const { return !(*this s); } void string::swap(string s) { std::swap(_str, s._str); std::swap(_size, s._size); std::swap(_capacity, s._capacity); } //string::string(const string s) //{ // _str new char[s._capacity 1]; // memcpy(_str, s._str, s._size 1); // _size s._size; // _capacity s._capacity; //} //现代写法 string::string(const string s) { string tmp(s._str); swap(tmp); } //string string::operator(const string s) //{ // if (this ! s) // { // char *tmp new char[s._capacity 1]; // memcpy(tmp, s._str, s._size 1); // delete[] _str; // _str tmp; // _size s._size; // _capacity s._capacity; // } // return *this; //} string string::operator(string s) { swap(s); return *this; } void string::clear() { _str[0] \0; _size 0; } ostream operator(ostream out, const string s) { // out s.c_str() endl;//遇到\0就停止打印 for (size_t i 0; i s.size(); i) { out s[i]; } return out; } istream operator(istream in, string s) { s.clear(); char buff[128]; int i 0; char ch in.get(); while (ch ! ch ! \n) { buff[i] ch; if (i 127) { buff[i] \0; s buff; i 0; } ch in.get(); } if (i 0) { buff[i] \0; s buff; } return in; } istream getline(istream is, string str, char delim) { str.clear(); char buff[128]; int i 0; char ch is.get(); while (ch ! delim) { buff[i] ch; if (i 127) { buff[i] \0; str buff; i 0; } ch is.get(); } if (i 0) { buff[i] \0; str buff; } return is; } void swap(string x, string y) { x.swap(y); } }