C++容器——string的基础实现(下)

📅 2026/6/30 22:40:48
C++容器——string的基础实现(下)
上篇博客主要讲了std标准库中string容器的基本使用方法。为了更加透彻的理解string同时也是增强基础代码能力所以来对string进行一个基本的实现。一.String的基础实现这里先放写好的string.hstring.cpp和test.cpp代码。#pragma once #includeiostream #includestring #includeassert.h #includecstring using namespace std; namespace Practice { class string { public: typedef char* iterator; typedef const char* const_iterator; string(); string(const char* str); string(size_t n , char c); string(const string s); string(const string s, size_t pos, size_t len npos); string operator(const string s); ~string(); size_t size() const; size_t capacity() const; char* c_str() const; char operator[](size_t i); const char operator[](size_t i) const; iterator begin(); const_iterator begin()const; iterator end(); const_iterator end()const; void reserve(size_t target_capacity); void push_back(const char c); void pop_back(); void append(const char* str); string operator(const char* str); string operator(const char c); size_t find(const char* s, size_t pos 0)const; size_t find(const char c, size_t pos 0)const; string insert(size_t pos,const char* str); string insert(size_t pos,const char c); string erase(size_t pos 0, size_t len npos); 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; void clear(); private: char* _str; size_t _size 0; size_t _capacity 15; public: static size_t npos; }; ostream operator (ostream out, const string s); istream operator(istream in, string s); istream getline(istream in, string s,char delim \n); }string.h文件中主要包含了对所实现的各个函数的声明。#define _CRT_SECURE_NO_WARNINGS 1 #includestring.h namespace Practice { size_t string::npos -1; string::string() :_str(new char[1]) , _size(0) , _capacity(15) { _str[0] \0; } string::string(const char* str) :_size(strlen(str)) { if (_size 15) { _capacity _size; } else { _capacity 15; } _str new char[_capacity1]; strcpy(_str, str); } string::string(size_t n, char c) :_size(n) { if (n 15) { _capacity n; } else { _capacity 15; } _str new char[_capacity 1]; for (size_t i 0; i n; i) { _str[i] c; } _str[_size] \0; } string::string(const string s) { _str new char[s._capacity 1]; memcpy(_str, s._str, s._size1); _size s._size; _capacity s._capacity; } string::string(const string s, size_t pos, size_t len ) { assert(pos 0 pos s._size); if (len npos || len (s._size - pos)) { _capacity s._size - pos; } else { _capacity len; } _str new char[_capacity 1]; memcpy(_str, s._str pos, _capacity); _size _capacity; _str[_size] \0; } string string::operator(const string s) { char* tmp new char[s._capacity 1]; memcpy(tmp, s._str, s._size1); delete[] _str; _str tmp; _size s._size; _capacity s._capacity; return *this; } string::~string() { delete[] _str; _size _capacity 0; } size_t string::size() const { return _size; } size_t string::capacity() const { return _capacity; } char* string::c_str() const { return _str; } char string::operator[](size_t i) { assert(i 0 i _size); return _str[i]; } const char string::operator[](size_t i) const { assert(i 0 i _size); return _str[i]; } string::iterator string::begin() { return _str; } string::const_iterator string::begin() const { return _str; } string::iterator string::end() { return _str _size; } string::const_iterator string::end()const { return _str _size; } void string::reserve(size_t target_capacity) { if (target_capacity _capacity) { _capacity target_capacity; char* tmp new char[_capacity1]; memcpy(tmp, _str, _size1); delete[] _str; _str tmp; return; } else { return; } } void string::push_back(const char c) { if (_size _capacity) { size_t newcapacity (_capacity 0 ? 15:_capacity * 2); reserve(newcapacity); } _str[_size] c; _str[_size] \0; } void string::pop_back() { assert(_size ! 0); _size--; _str[_size] \0; } void string::append(const char* str) { size_t len strlen(str); if (_size len _capacity) { reserve(_sizelen); } strcpy(_str _size, str); _size len; } string string::operator(const char* str) { (*this).append(str); return *this; } string string::operator(const char c) { (*this).push_back(c); return *this; } string string::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 pos len) { _str[end] _str[end - len]; end--; } memcpy(_str pos, str, len); _size len; _str[_size] \0; return *this; } string string::insert(size_t pos ,const char c) { assert(pos _size); if (_size _capacity) { size_t newcapacity (_capacity 0 ? 15 : _capacity * 2); reserve(newcapacity); } size_t end _size1; while (end pos) { _str[end] _str[end-1]; end--; } _str[pos] c; _str[_size] \0; return *this; } string string::erase(size_t pos , size_t len) { assert(pos 0 pos _size); if (len npos || len (_size - pos)) { _size pos; _str[pos] \0; } else { while (poslen _size) { _str[pos] _str[pos len]; pos; } _size _size - len; _str[_size] \0; } return *this; } size_t string::find(const char* s, size_t pos)const { assert(pos _size); char* str strstr(_str, s); if (str nullptr) { return npos; } else { return str - _str; } } size_t string::find(const char c, size_t pos)const { assert(pos _size); for (size_t i 0; i _size; i) { if (_str[i] c) { return i; } } return npos; } bool string::operator(const string s)const { size_t l1 0; size_t l2 0; while (l1 _size l2 s.size()) { if (_str[l1] s[l2]) { return false; } else if (_str[l1] s[l2]) { return false; } else { l1; l2; } } return ((l1 _size) (l2 s.size())); } bool string::operator!(const string s)const { return !((*this) s); } bool string::operator(const string s)const { size_t l1 0; size_t l2 0; while (l1 _size l2 s.size()) { if (_str[l1] s[l2]) { return true; } else if (_str[l1] s[l2]) { return false; } else { l1; l2; } } return (l2 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 || (*this) s; } ostream operator (ostream out, const string s) { for (size_t i 0; i s.size(); i) { cout s[i]; } return out; } void string::clear() { _str[0] \0; _size 0; } //以下的两种写法会涉及到开辟空间多次的问题 //istream operator(istream in, string s) //{ // s.clear(); // char ch in.get(); // while (ch ! ch ! \n) // { // s ch; // ch in.get(); // } // return in; //} istream operator(istream in, string s) { s.clear(); char tmp[256]; size_t i 0; char ch in.get(); while (ch ! ch ! \n) { tmp[i] ch; if (i 255) { tmp[i] /0; s tmp; i 0; } ch in.get(); } if (i 0) { tmp[i] \0; s tmp; } return in; } //istream getline(istream in, string s, char delim) //{ // s.clear(); // char ch in.get(); // while (ch ! delim) // { // s ch; // ch in.get(); // } // return in; //} istream getline(istream in, string s, char delim) { s.clear(); char tmp[256]; size_t i 0; char ch in.get(); while (ch ! delim) { tmp[i] ch; if (i 255) { tmp[i] /0; s tmp; i 0; } ch in.get(); } if (i 0) { tmp[i] \0; s tmp; } return in; } }string.cpp主要是对于各个函数的实现过程。test.cpp主要是对于实现函数的测试过程。#define _CRT_SECURE_NO_WARNINGS 1 #includestring.h namespace Practice { void test1() { //构造函数的测试 string s1; string s2(hello world); string s3(20, c); string s4 s2; string s5(s3, 0, 5); } void test2() { string s1(Hello World); for (size_t i 0; i s1.size(); i) { s1[i]; cout s1[i]; } cout endl; string s2(Hello World); string::iterator it s2.begin(); while (it ! s2.end()) { cout (*it); it ; } cout endl; const string s3(Hello World); string::const_iterator cit s3.begin(); while (cit ! s3.end()) { //cout (*cit);//表达式必须是可修改的左值 cout *it; cit; } } void test3() { string s1; cout size: s1.size() endl; cout capacity: s1.capacity() endl; cout endl; s1.reserve(50); cout size: s1.size() endl; cout capacity: s1.capacity() endl; cout endl; s1.reserve(5);//没有缩容 cout size: s1.size() endl; cout capacity: s1.capacity() endl; cout endl; s1.push_back(a); s1.push_back(b); s1.push_back(c); s1.push_back(d); s1.push_back(e); s1.push_back(f); cout s1.c_str() endl; cout size: s1.size() endl; cout capacity: s1.capacity() endl; cout endl; s1.append(Hello World); cout s1.c_str() endl; cout size: s1.size() endl; cout capacity: s1.capacity() endl; cout endl; s1 x; s1 cccccccc; cout s1.c_str() endl; cout size: s1.size() endl; cout capacity: s1.capacity() endl; cout endl; s1.pop_back(); s1.pop_back(); s1.pop_back(); s1.pop_back(); s1.pop_back(); cout s1.c_str() endl; cout size: s1.size() endl; cout capacity: s1.capacity() endl; cout endl; } void test4() { string s1(xxxxxxxxxx); s1.insert(2, abcdef); cout s1.c_str() endl; s1.insert(0, y); cout s1.c_str() endl; s1.insert(0, 123456); cout s1 endl; } void test5() { string s1(abcdefg); s1.erase(0, 2); cout s1 endl; s1.erase(); cout s1 endl; } void test6() { string s1; string s2; cin s1 s2; cout s1 endl; cout s2 endl; //string s3; //getline(cin, s3, !); //cout s3 endl; } } int main() { //Practice::test1(); //Practice::test2(); //Practice::test3(); //Practice::test4(); //Practice::test5(); Practice::test6(); return 0; }二.各个函数的讲解namespace Practice { class string { public: typedef char* iterator; typedef const char* const_iterator; string(); string(const char* str); string(size_t n , char c); string(const string s); string(const string s, size_t pos, size_t len npos); string operator(const string s); ~string(); size_t size() const; size_t capacity() const; char* c_str() const; char operator[](size_t i); const char operator[](size_t i) const; iterator begin(); const_iterator begin()const; iterator end(); const_iterator end()const; void reserve(size_t target_capacity); void push_back(const char c); void pop_back(); void append(const char* str); string operator(const char* str); string operator(const char c); size_t find(const char* s, size_t pos 0)const; size_t find(const char c, size_t pos 0)const; string insert(size_t pos,const char* str); string insert(size_t pos,const char c); string erase(size_t pos 0, size_t len npos); 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; void clear(); private: char* _str; size_t _size 0; size_t _capacity 15; public: static size_t npos; }; ostream operator (ostream out, const string s); istream operator(istream in, string s); istream getline(istream in, string s,char delim \n); }首先创建了一个命名空间为了和std标准库中的string区分所以用命名空间来区分。2.1私有成员变量和静态成员函数nposprivate: char* _str; size_t _size 0; size_t _capacity 15; public: static size_t npos;string的底层实现类似于C语言基础数据结构中的顺序表可以将其理解为一个存放字符串的顺序表。包含一个指向字符串数组的char*指针_str一个指向最后一个元素的下标_size一个指示当前数组容量的_capacity。在string中还存在一个size_t 类型的静态成员变量npos为-1用来取到字符串的最后一个元素位置。2.2默认成员函数string(); string(const char* str); string(size_t n , char c); string(const string s); string(const string s, size_t pos, size_t len npos); string operator(const string s); ~string();其次是默认成员函数的相关实现包括构造函数、拷贝构造函数、赋值运算符重载和析构函数。2.2.1构造函数构造函数主要重载了三种类型1.空字符串2.常量字符串构造3.n个字符c构造string::string() :_str(new char[1]) , _size(0) , _capacity(15) { _str[0] \0; } string::string(const char* str) :_size(strlen(str)) { if (_size 15) { _capacity _size; } else { _capacity 15; } _str new char[_capacity1]; strcpy(_str, str); } string::string(size_t n, char c) :_size(n) { if (n 15) { _capacity n; } else { _capacity 15; } _str new char[_capacity 1]; for (size_t i 0; i n; i) { _str[i] c; } _str[_size] \0; }首先是空字符串构造只需要开辟一个空间来存放 ’\0‘即可。在string中所实现的_size和_capacity均不统计’\0‘的位置。所以之后的函数的实现过程中也要注意在_size位置存放的是\0数据增删查改的过程中要时刻注意这个\0。第二是常量字符串的构造首先利用strlen函数统计出该常量字符串有多少字符之后利用进行空间开辟。因为要存放\0所以需要多开辟一个空间。之后利用strcpy函数进行数据拷贝即可。第三是n个字符c的构造首先根据传递的形参n来构造一个能存放下n个字符的数组空间之后利用for循环n次进行数据的存放即可。注意最后一个位置的\0。2.2.2拷贝构造string::string(const string s) { _str new char[s._capacity 1]; memcpy(_str, s._str, s._size1); _size s._size; _capacity s._capacity; } string::string(const string s, size_t pos, size_t len ) { assert(pos 0 pos s._size); if (len npos || len (s._size - pos)) { _capacity s._size - pos; } else { _capacity len; } _str new char[_capacity 1]; memcpy(_str, s._str pos, _capacity); _size _capacity; _str[_size] \0; }拷贝构造实现了两个重载方式第一种是单纯利用一个string对象的拷贝第二种实现了利用string对象的某一部分进行拷贝。第一种由于明确了解需要拷贝的对象中有多少内容、多少空间大小所以只需要依次按照该对象的各项参数进行拷贝即可。memcpy拷贝数据的过程中要注意多拷贝一个数据所以是_size1来拷贝这样会将末尾的\0进行拷贝或者拷贝_size个字符然后手动将_size位置的字符置为\0。第二种给定了要拷贝字符串的长度。如果该长度是缺省值npos或者给定的要拷贝对象的长度大于该字符串末尾到pos的距离就说明该字符串需要从当前位置拷贝到字符串末尾。这样的需要对当前对象开辟一个由拷贝对象字符串末尾到pos位置距离长度的数组。如果len小于这个长度只需要开辟len个字符数组的空间即可。之后的拷贝过程类似。注意在_size位置设置’\0‘。注意这个拷贝构造不能通过移动_size1个字符来确保移动了\0因为挪动的数据不一定达到末尾。2.2.3赋值运算符重载string string::operator(const string s) { char* tmp new char[s._capacity 1]; memcpy(tmp, s._str, s._size1); delete[] _str; _str tmp; _size s._size; _capacity s._capacity; return *this;赋值运算符重载的实现方式和构造函数较为类似。唯一的不同是原本的对象中有无资源需要删除。创建一个临时对象空间大小取决于要赋值的对象。之后继续进行数据的挪动即可。最后将原来的数据空间释放并指向新开辟的数据空间即可。2.2.4析构函数string::~string() { delete[] _str; _size _capacity 0; }利用delete[]对类中的数组空间进行释放之后将_size和_capacity置零可有可无即可。2.3 C语言接口函数和取数值相关函数size_t string::size() const { return _size; } size_t string::capacity() const { return _capacity; } char* string::c_str() const { return _str; }三个函数分别返回_size_capacity和_str为外部提供访问接口。均设置为const成员函数来为const string对象提供访问机会。2.4 遍历相关函数遍历相关函数包括方括号重载迭代器的实现typedef char* iterator; typedef const char* const_iterator; char string::operator[](size_t i) { assert(i 0 i _size); return _str[i]; } const char string::operator[](size_t i) const { assert(i 0 i _size); return _str[i]; } string::iterator string::begin() { return _str; } string::const_iterator string::begin() const { return _str; } string::iterator string::end() { return _str _size; } string::const_iterator string::end()const { return _str _size; }首先是方括号重载实现两个版本分别对string对象和const string对象提供[]下标访问的接口。string对象返回char用来可以对内容进行修改const string对象不能进行修改。其次是iterator迭代器目前可以用较为简单的方式来实现直接用typedef char* iterator即可。同时const_iterator用const char* 来实现即可。begin函数和end函数都重载了const_iterator版本。begin返回数组的首地址即可end返回指向数组的最后一个元素的位置即_str_size。2.5 reserve开辟空间函数void string::reserve(size_t target_capacity) { if (target_capacity _capacity) { _capacity target_capacity; char* tmp new char[_capacity1]; memcpy(tmp, _str, _size1); delete[] _str; _str tmp; return; } else { return; }std库中的reserve开辟空间只可以将容量增大不可缩小。传参为目标空间大小。如果该参数大于当前对象的空间就进行空间增大否则不改变当前空间大小。改变空间大小开辟一个新的数组tmp之后利用memcpy将原数组中的元素拷贝到新数组中。释放原数组的空间并将_str赋值为tmp即可。在这里注意开辟空间时仍要多开辟一个用来存放\0同时注意删除原数组中的元素防止内存泄漏。2.6 增加删除相关函数2.6.1 push_back()和pop_back()void string::push_back(const char c) { if (_size _capacity) { size_t newcapacity (_capacity 0 ? 15:_capacity * 2); reserve(newcapacity); } _str[_size] c; _str[_size] \0; } void string::pop_back() { assert(_size ! 0); _size--; _str[_size] \0; }push_back函数用来向字符串末尾位置添加一个字符首先判断容量大小是否足够如果不够采用二倍扩容机制。之后在字符串的_size位置添加一个字符后_size自增之后在结束位置添加一个\0即可。pop_back函数用来删除字符串结尾的一个元素。要注意当前字符串不能仅有\0如果只有\0表示当前字符串为空不能进行删除。其余情况仅需将_size向前挪动一个字符然后将当前位置赋值为\0即可。2.6.2 append()void string::append(const char* str) { size_t len strlen(str); if (_size len _capacity) { reserve(_sizelen); } strcpy(_str _size, str); _size len; }append函数用来向字符串末尾添加一串字符。首先需要判断容量问题如果当前位置的坐标加上所需添加的字符串长度大于当前容量那么需要扩容。之后将字符串依次拷贝到当前对象的字符串末尾即可。最后改变_size的指向。2.6.3 operatorstring string::operator(const char* str) { (*this).append(str); return *this; } string string::operator(const char c) { (*this).push_back(c); return *this; }operator的重载函数重载了两个版本。分别是在当前对象末尾添加一串字符和一个字符。分别调用push_back和append即可。2.6.4 insert和erasestring string::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 pos len) { _str[end] _str[end - len]; end--; } memcpy(_str pos, str, len); _size len; _str[_size] \0; return *this; } string string::insert(size_t pos ,const char c) { assert(pos _size); if (_size _capacity) { size_t newcapacity (_capacity 0 ? 15 : _capacity * 2); reserve(newcapacity); } size_t end _size1; while (end pos) { _str[end] _str[end-1]; end--; } _str[pos] c; _str[_size] \0; return *this; } string string::erase(size_t pos , size_t len) { assert(pos 0 pos _size); if (len npos || len (_size - pos)) { _size pos; _str[pos] \0; } else { while (poslen _size) { _str[pos] _str[pos len]; pos; } _size _size - len; _str[_size] \0; } return *this; }insert函数重载了两个版本分别是在某一个下标的位置插入一个字符在某个下标的位置插入一串字符。首先讲解在某个下表的位置插入一个字符对应上面代码的第二个函数。首先检查空间大小如果不够就进行扩容。定义变量end赋值为_size1表示当前字符串末尾的下一个位置。开始循环赋值将end的位置赋值为end-1位置的值。循环结束条件为endpos即end-1 pos已经赋值给过end的位置了pos位置的原数据已经向后进行挪动。之后将pos位置的数据赋值为要插入的字符c即可最后改变_size的大小即可。之后是在某一下标位置插入一串字符。仍然先进行判断容量大小开辟空间。于上面的思路类似只不过上面的函数只需要向后依次挪动1个字符这个函数需要依次向后挪动要插入的字符串长度的字符。所以定义变量end _sizelen。将end位置赋值为end-len的值。循环的结束条件为end - len pos但是要十分注意这样的写法是错误的因为end的类型为size_t可能会出现end-pos -1但size_t的-1表示为整形的最大值不会结束判断就会导致死循环的发生。所以为了避免这种情况出现判断条件写为end poslen。之后将要插入的字符串依次拷贝即可。最后挪动_size。erase函数要实现在某个下标删除len个字符长度。如果要删除的字符长度大于该字符串之后的内容以及len为缺省值npos那么表示要将该下标位置之后的值全部删除。直接挪动_size为_size-len并将_size位置的值赋值为\0。如果只是删除一段位置就需要依次向前挪动数据。将pos位置的值赋值为poslenpos自增指向下一个位置。循环结束条件为poslen_size表示已经遍历到最后一个数据。2.6.5 find查找函数size_t string::find(const char* s, size_t pos)const { assert(pos _size); char* str strstr(_str, s); if (str nullptr) { return npos; } else { return str - _str; } } size_t string::find(const char c, size_t pos)const { assert(pos _size); for (size_t i 0; i _size; i) { if (_str[i] c) { return i; } } return npos; }查找函数实现了两个重载分别表示在当前对象字符串的某个下标及之后的位置寻找字符串s和寻找单字符c。如果是寻找字符串s利用了strstr函数如果找到了该函数会返回第一次出现该字符串的地址减去首地址就得到了下标。如果没找到就会返回空指针。如果是寻找单字符for循环遍历即可。2.6.6 判断大小相关函数bool string::operator(const string s)const { size_t l1 0; size_t l2 0; while (l1 _size l2 s.size()) { if (_str[l1] s[l2]) { return false; } else if (_str[l1] s[l2]) { return false; } else { l1; l2; } } return ((l1 _size) (l2 s.size())); } bool string::operator!(const string s)const { return !((*this) s); } bool string::operator(const string s)const { size_t l1 0; size_t l2 0; while (l1 _size l2 s.size()) { if (_str[l1] s[l2]) { return true; } else if (_str[l1] s[l2]) { return false; } else { l1; l2; } } return (l2 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 || (*this) s; }判断大小相关的函数只需要实现判断相等和判断小于即可函数复用来实现其他相关函数。首先是判断相等遍历两个字符串中的每个字符如果出现不相等的情况返回false相等的情况两个下标均向后走。最后出了循环之后当两个下标位置均走到了两个字符串的末尾表示两个字符串长度相同并且每个字符都相同返回true。之后是判断小于依次遍历每个字符在中间过程中只要出现了一次字符小于另一个就直接返回true出现大于返回false。两个字符相同的情况均向后走。循环结束后会出现三种情况1. 字符串s1长度小于字符串s2此时指向s2的下标小于s2的size返回true2.字符串s1的长度和s2相等此时返回false3.字符串s1的长度大于s2也返回false。所以返回判断条件可以只写return (l2 s.size());2.6.7 流插入 和 流提取函数的重载ostream operator (ostream out, const string s) { for (size_t i 0; i s.size(); i) { cout s[i]; } return out; } void string::clear() { _str[0] \0; _size 0; } //以下的两种写法会涉及到开辟空间多次的问题 //istream operator(istream in, string s) //{ // s.clear(); // char ch in.get(); // while (ch ! ch ! \n) // { // s ch; // ch in.get(); // } // return in; //} istream operator(istream in, string s) { s.clear(); char tmp[256]; size_t i 0; char ch in.get(); while (ch ! ch ! \n) { tmp[i] ch; if (i 255) { tmp[i] /0; s tmp; i 0; } ch in.get(); } if (i 0) { tmp[i] \0; s tmp; } return in; } //istream getline(istream in, string s, char delim) //{ // s.clear(); // char ch in.get(); // while (ch ! delim) // { // s ch; // ch in.get(); // } // return in; //} istream getline(istream in, string s, char delim) { s.clear(); char tmp[256]; size_t i 0; char ch in.get(); while (ch ! delim) { tmp[i] ch; if (i 255) { tmp[i] /0; s tmp; i 0; } ch in.get(); } if (i 0) { tmp[i] \0; s tmp; } return in; } }首先是流提取运算符在之前的一个小练习日期类的实现过程中 流插入和提取均实现为了友元函数因为要访问私有成员变量。而在这里可以通过c_str函数访问到_str这个字符数组指针所以可以不用设置为私有。同时由于[]运算符重载的存在可以通过[]下标直接访问依次输出每个字符即可。针对流提取运算符的重载利用插入流cin中内部函数get。该函数可以提取到空格和换行符。整体思路为输入一串字符利用一个字符变量来接收字符如果该字符不是空格或者换行符将其插入到string对象的末尾之后再次接收该字符直到循环结束。geiline也是类似的思路只是将循环判断结束的条件变为手动指定的标识符。上述方法由于输入的字符长度不确定如果输入字符长度过长会导致多次扩容的问题可能会导致其效率降低。针对此问题对流插入和geiline函数进行一定程度的优化首先创造一个字符串数组可以将其开辟的大一些。然后在这个字符串数组中不断地尾插字符如果该字符到了最后一位则在最后一位插入\0然后将其插入到string对象中之后重新赋值该数组。如果遇到了循环结束的条件则直接在该数组的当前位置插入\0尾插到string对象中。该方法可以降低string对象扩容的次数。2.6.7 拷贝构造函数和赋值运算符重载的现代写法void string::Swap(string s) { std::swap(_str, s._str); std::swap(_size, s._size); std::swap(_capacity, s._capacity); } string::string(const char* str) { string tmp(str); Swap(tmp); } string string::operator(string s) { Swap(s); return *this; }首先实现了一个交换函数用来交换两个string对象之间的数组地址、_size和_capacity。拷贝构造利用要拷贝对象的_str构造一个临时对象tmp只需要调用tmp函数将这个临时对象的成员变量换到this对象中即可可完成拷贝构造。赋值运算符重载传参只需要传一个string对象s注意不需要引用然后调用Swap交换即可。由于传递的形参不是引用类型是原对象的一个拷贝所以能在完成赋值的情况下不影响原对象。至此完成了string类的基础实现虽说和库中的string类仍存在不小的差别但整体的实现思路和对于类和对象的掌握与理解更进一步得到了加深尤其是构造、拷贝构造、命名空间、数据的增删查改、数据边界等都有了进一步的巩固加深。