【C++】string的模拟实现
文章目录
string的模拟实现
1.1 经典的string类问题
上面已经对string类进行了简单的介绍,大家只要能够正常使用即可。在面试中,面试官总喜欢让 学生自己来模拟实现string类,最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析 构函数。大家看下以下string类的实现是否有问题?
// 为了和标准库区分,此处使用StringclassString{public:/*String() :_str(new char[1]) {*_str = '\0';} *///String(const char* str = "\0") 错误示范//String(const char* str = nullptr) 错误示范String(constchar* str =""){// 构造String类对象时,如果传递nullptr指针,可以认为程序非if(nullptr== str){assert(false);return;} _str =newchar[strlen(str)+1];strcpy(_str, str);}~String(){if(_str){delete[] _str; _str =nullptr;}}private:char* _str;};// 测试voidTestString(){ String s1("hello bit!!!"); String s2(s1);}说明:上述String类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认 的,当用s1构造s2时,编译器会调用默认的拷贝构造。最终导致的问题是,s1、s2共用同一块内 存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。
1.2 浅拷贝
浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致 多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该 资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规。
就像一个家庭中有两个孩子,但父母只买了一份玩具,两个孩子愿意一块玩,则万事大吉,万一 不想分享就你争我夺,玩具损坏。
可以采用深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不要和其他对象共享。父 母给每个孩子都买一份玩具,各自玩各自的就不会有问题了。
1.3 深拷贝
如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给
出。一般情况都是按照深拷贝方式提供。
模拟实现
构造函数的模拟实现
//传统写法/*string(const string& s) { _str = new char[s._capacity + 1]; strcpy(_str, s._str); _size = s._size; _capacity = s._capacity; }*///s2(s1) tmp是s2//现代写法string(const string& s){ string tmp(s._str);//默认构造swap(tmp);//把tmp和s2交换}赋值重载的模拟实现
//s2=s1 赋值//传统写法/*string& operator=(const string& s) { if (this != &s) { delete[] _str; _str = new char[s._capacity + 1]; _size = s._size; _capacity = s._capacity; } return *this; }*///s1=s3//string& operator=(const string& s)//{// if (this != &s)// {// //把s3的内容都给tmp.然后s1与tmp交换// //string tmp(s._str); 调用默认构造// string tmp(s);//调用拷贝构造// swap(tmp);// }// //而且tmp出了作用域自动销毁// return *this;//}//最新版本的赋值 string&operator=(string tmp)//拷贝构造必须用引用,不然会无穷递归 但赋值不用{swap(tmp);return*this;}析构的模拟实现
~string(){delete[] _str; _str =nullptr; _capacity = _size =0;}clear的模拟实现
voidclear(){ _str[0]='\0'; _size =0;}reserve的模拟实现
void string::reserve(size_t n){if(n > _capacity){char* tmp =newchar[n +1];//永远多开一个空间strcpy(tmp, _str);delete[] _str; _str = tmp; _capacity = n;}}push_back的模拟实现
void string::push_back(char ch){if(_size == _capacity){reserve(_capacity ==0?4: _capacity *2);} _str[_size]= ch;++_size; _str[_size]='\0';}append的模拟实现
void string::append(constchar* str){ size_t len =strlen(str);if(_size + len > _capacity){reserve(_size + len > _capacity *2? _size + len : _capacity *2);}strcpy(_str + _size, str);//_str+size就是\0的位置 _size += len;}insert的模拟实现
void string::insert(size_t pos,char ch)//插字符{assert(pos <= _size);if(_size == _capacity){reserve(_capacity ==0?4: _capacity *2);} size_t end = _size+1;while(end > pos){ _str[end]= _str[end-1];//如果end=_size _str[end+1]=_str[size] 当end减到-1时与pos比较会隐式类型转换--end;} _str[pos]= ch;++_size;}void string::insert(size_t pos,constchar* str)//插字符串{assert(pos <= _size);//扩容 size_t len =strlen(str);if(_size + len > _capacity){reserve(_size + len > _capacity *2? _size + len : _capacity *2);} size_t end = _size + len;if(len ==0)return;while(end > pos + len-1)//end=pos+len时也要继续{ _str[end]= _str[end-len];--end;}for(size_t i =0; i < len;++i){ _str[pos+i]= str[i];} _size += len;}erase的模拟实现
void string::erase(size_t pos, size_t len )//缺省参数在声明的时候给{assert(pos < _size);if(len > _size - pos){ _str[pos]='\0'; _size =pos;}else{for(size_t i = pos + len;i<=_size ; i++){ _str[i - len]= _str[i];} _size -= len;}}find的模拟实现
size_t string::find(char ch, size_t pos){for(size_t i = pos; i < _size; i++){if(_str[i]== ch)return i;}return npos;} size_t string::find(constchar* str, size_t pos){assert(pos < _size);constchar* ptr =strstr(_str + pos, str);//strstr返回第一个字串位置if(ptr ==nullptr){return npos;}else{return ptr - _str;}}substr的模拟实现
string string::substr(size_t pos , size_t len ){assert(pos < _size);//len太大就调整一下if(len > _size - pos){ len = _size - pos;} string sub; sub.reserve(len);for(size_t i=0;i<len;++i){ sub+= _str[pos + i];}return sub;}比较运算符的模拟实现
booloperator<(const string& s1,const string& s2){returnstrcmp(s1.c_str(), s2.c_str())<0;}booloperator==(const string& s1,const string& s2){returnstrcmp(s1.c_str(), s2.c_str())==0;}booloperator>(const string& s1,const string& s2){return!(s1 < s2 || s1 == s2);}booloperator<=(const string& s1,const string& s2){return s1 < s2 || s1 == s2;}booloperator>=(const string& s1,const string& s2){return!(s1 < s2);}booloperator!=(const string& s1,const string& s2){return!(s1 == s2);}流插入的模拟实现
ostream&operator<<(ostream& out,const string& s){for(auto ch : s){ out << ch;}return out;}流提取的模拟实现
istream&operator>>(istream& in, string& s){ s.clear();constint N =256;char buff[N];int i =0;char ch;//in >> ch; cin默认提取不到空格跟换行 ch = in.get();while(ch !=' '&& ch !='\n'){ buff[i++]= ch;if(i == N -1){ buff[i]='\0'; s += buff; i =0;} s += ch;//in >> ch; ch = in.get();if(i >0){ buff[i]='\0'; s += buff;}}return in;}完整代码
string.cpp
#include"string.h"namespace bit {void string::reserve(size_t n){if(n > _capacity){char* tmp =newchar[n +1];//永远多开一个空间strcpy(tmp, _str);delete[] _str; _str = tmp; _capacity = n;}}void string::push_back(char ch){if(_size == _capacity){reserve(_capacity ==0?4: _capacity *2);} _str[_size]= ch;++_size; _str[_size]='\0';} string& string::operator+=(char ch){push_back(ch);return*this;}void string::append(constchar* str){ size_t len =strlen(str);if(_size + len > _capacity){reserve(_size + len > _capacity *2? _size + len : _capacity *2);}strcpy(_str + _size, str);//_str+size就是\0的位置 _size += len;} string& string::operator+=(constchar* str){append(str);return*this;}void string::insert(size_t pos,char ch){assert(pos <= _size);if(_size == _capacity){reserve(_capacity ==0?4: _capacity *2);} size_t end = _size+1;while(end > pos){ _str[end]= _str[end-1];//如果end=_size _str[end+1]=_str[size] 当end减到-1时与pos比较会隐式类型转换--end;} _str[pos]= ch;++_size;}void string::insert(size_t pos,constchar* str){assert(pos <= _size);//扩容 size_t len =strlen(str);if(_size + len > _capacity){reserve(_size + len > _capacity *2? _size + len : _capacity *2);} size_t end = _size + len;if(len ==0)return;while(end > pos + len-1)//end=pos+len时也要继续{ _str[end]= _str[end-len];--end;}for(size_t i =0; i < len;++i){ _str[pos+i]= str[i];} _size += len;}void string::erase(size_t pos, size_t len )//缺省参数在声明的时候给{assert(pos < _size);if(len > _size - pos){ _str[pos]='\0'; _size =pos;}else{for(size_t i = pos + len;i<=_size ; i++){ _str[i - len]= _str[i];} _size -= len;}} size_t string::find(char ch, size_t pos){for(size_t i = pos; i < _size; i++){if(_str[i]== ch)return i;}return npos;} size_t string::find(constchar* str, size_t pos){assert(pos < _size);constchar* ptr =strstr(_str + pos, str);//strstr返回第一个字串位置if(ptr ==nullptr){return npos;}else{return ptr - _str;}} string string::substr(size_t pos , size_t len ){assert(pos < _size);//len太大就调整一下if(len > _size - pos){ len = _size - pos;} string sub; sub.reserve(len);for(size_t i=0;i<len;++i){ sub+= _str[pos + i];}return sub;}voidtest_string1(){ string s1; string s2("hello world"); cout << s1.c_str()<< endl; cout << s2.c_str()<< endl;for(size_t i =0; i < s2.size();++i){ s2[i]+=2;} cout << s2.c_str()<< endl;for(auto e : s2){ cout << e <<" ";} cout << endl; string::iterator it = s2.begin();while(it != s2.end()){ cout <<*it <<" ";++it;}}voidtest_string2(){ string s1("hello wwww"); s1 +='x'; cout << s1.c_str()<< endl; s1.insert(3,"sss"); cout << s1.c_str()<< endl; s1.erase(3,2); cout << s1.c_str()<< endl;}voidtest_string3(){ string s("666.cpp.zip"); size_t pos =s.find('.'); string suffix = s.substr(pos); cout << suffix.c_str()<< endl; string copy(s); cout << copy.c_str()<< endl;}booloperator<(const string& s1,const string& s2){returnstrcmp(s1.c_str(), s2.c_str())<0;}booloperator==(const string& s1,const string& s2){returnstrcmp(s1.c_str(), s2.c_str())==0;}booloperator>(const string& s1,const string& s2){return!(s1 < s2 || s1 == s2);}booloperator<=(const string& s1,const string& s2){return s1 < s2 || s1 == s2;}booloperator>=(const string& s1,const string& s2){return!(s1 < s2);}booloperator!=(const string& s1,const string& s2){return!(s1 == s2);} ostream&operator<<(ostream& out,const string& s){for(auto ch : s){ out << ch;}return out;} istream&operator>>(istream& in, string& s){ s.clear();constint N =256;char buff[N];int i =0;char ch;//in >> ch; cin默认提取不到空格跟换行 ch = in.get();while(ch !=' '&& ch !='\n'){ buff[i++]= ch;if(i == N -1){ buff[i]='\0'; s += buff; i =0;} s += ch;//in >> ch; ch = in.get();if(i >0){ buff[i]='\0'; s += buff;}}return in;}voidtest_string4(){ string s1("hello"); cin >> s1 ; cout << s1<<endl; cout <<"he";}}intmain(){ bit::test_string4();}string.h
#pragmaonce#include<iostream>#include<assert.h>#include<string.h>usingnamespace std;namespace bit {classstring{public://string()// : _str(new char[1]{'\0'})//不能给nullptr 因为会对空指针解引用// , _size(0)// ,_capacity(0)//{}//短小频繁调用的函数,可以直接放到类里面,默认是inlinetypedefchar* iterator;//迭代器就是对各类型的封装 iterator begin(){return _str;} iterator end(){return _str + _size;}typedefconstchar* const_iterator; const_iterator begin()const{return _str;} const_iterator end()const{return _str + _size;} size_t capacity()const{return _capacity;}voidreserve(size_t n);voidpush_back(char ch);voidappend(constchar* str); string&operator+=(char ch); string&operator+=(constchar* str);voidinsert(size_t pos,char ch);voidinsert(size_t pos,constchar* str);voiderase(size_t pos, size_t len = npos); size_t find(char ch, size_t pos =0); size_t find(constchar* str, size_t pos =0); string substr(size_t pos =0, size_t len = npos);string(constchar* str="")//""就相当于一个\0{ _size =strlen(str); _capacity = _size;//capacity不加1,因为capacity不包含\0 _str =newchar[_capacity +1];//空间多开一个放\0strcpy(_str, str);//会把\0也拷贝过去}//s2(s1) 拷贝构造//传统写法/*string(const string& s) { _str = new char[s._capacity + 1]; strcpy(_str, s._str); _size = s._size; _capacity = s._capacity; }*///s2(s1) tmp是s2//现代写法string(const string& s){ string tmp(s._str);//默认构造swap(tmp);//把tmp和s2交换}//s2=s1 赋值//传统写法/*string& operator=(const string& s) { if (this != &s) { delete[] _str; _str = new char[s._capacity + 1]; _size = s._size; _capacity = s._capacity; } return *this; }*///s1=s3//string& operator=(const string& s)//{// if (this != &s)// {// //把s3的内容都给tmp.然后s1与tmp交换// //string tmp(s._str); 调用默认构造// string tmp(s);//调用拷贝构造// swap(tmp);// }// //而且tmp出了作用域自动销毁// return *this;//}//最新版本的赋值 string&operator=(string tmp)//拷贝构造必须用引用,不然会无穷递归 但赋值不用{swap(tmp);return*this;}voidswap(string& s){ std::swap(_str, s._str); std::swap(_size, s._size); std::swap(_capacity, s._capacity);}~string(){delete[] _str; _str =nullptr; _capacity = _size =0;}voidclear(){ _str[0]='\0'; _size =0;}constchar*c_str()const{return _str;} size_t size()const{return _size;}char&operator[](size_t pos){assert(pos < _size);return _str[pos];}constchar&operator[](size_t pos)const{assert(pos < _size);return _str[pos];}private:char* _str=nullptr; size_t _size=0; size_t _capacity=0;//下列这也算定义staticconst size_t npos=-1;//静态成员类外初始化,但是这里可以也直接给,static const才可以在这里直接初始化,而且只有整型可以这么玩};//const size_t npos = -1;}(tmp);
return *this; } void swap(string& s) { std::swap(_str, s._str); std::swap(_size, s._size); std::swap(_capacity, s._capacity); } ~string() { delete[] _str; _str = nullptr; _capacity = _size = 0; } void clear() { _str[0] = '\0'; _size = 0; } const char* c_str() const { return _str; } 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]; } private: char* _str=nullptr; size_t _size=0; size_t _capacity=0; //下列这也算定义 static const size_t npos=-1;//静态成员类外初始化,但是这里可以也直接给,static const才可以在这里直接初始化,而且只有整型可以这么玩 }; //const size_t npos = -1; }