C++----模拟实现string
模拟实现string,首先我们要知道成员变量有哪些:
class _string{private:char* _str;size_t capacity;//空间有多大size_t size;//有效字符多少const static size_t npos;};const size_t _string::npos=-1;//static在外面定义不需要带static,npos表示最大位置
接着写构造函数,在string中,初始化方式有无参的,有用字符串初始化的,这就要求我们写一个带缺省参数的默认构造函数:
string(const char* str=""):capacity(strlen(str)),size(capacity)
{_str=new char[capacity+1];strcpy(_str,str);
}
//为什么用"",因为它代表了\0,用它初始化再合适不过了
//为什么不用'\0'呢?因为这是一个char*,用'\0'就会用他的ASCII码值初始化
//为什么不用nullptr呢?因为在后面可能会用到_str的解引用,解引用空指针要出问题的
接下来写拷贝构造
string(const string& s)
{_str=new char[s.capacity+1];capacity=s.capacity;size=s.size;strcpy(_str,s._str);
}
赋值符号重载
//与单纯的拷贝构造不一样,复制重载原string对象一般都会有原始空间,要释放原始空间
const string& operator=(const string& s)
{//最重要的一点!一定要检查自赋值,不检查的话会导致空间释放使用未分配的空间if(this==&s)return *this;//并且释放原始空间和开辟新空间是有顺序的,开辟新空间一定要放在前面//避免开辟新空间失败导致原来空间也没有了!char *new_ptr=new char[s.size+1];strcpy(new_ptr,s._str);delete[]_str;_str=new_str;capacity = s.capacity;size = s.size;return *this;
}
如果我们要打印string对象的字符串呢?在没有重载流提取符号的情况下:
const char* c_str() const
{return _str;
}
开始定义增删查改部分;增:有push_back、append、+=、insert,直接先定义insert,其他的都可以复用insert函数,insert有插入字符版本和字符串版本:
void reserve(size_t n)
{if(capacity<n){char* new_ptr=new char[n+1];strpy(new_ptr,_ptr);delete[]_str;_str=new_ptr;capacity=n;}
}void resize(size_t n,char ch='\0')//给一个缺省参数
{//小于size实质上就是删除多余的字符if(n<size)_str[n]='\0'else{if(n>capacity)reserve(n);for(int i=size;i<n;i++){_str[i]=ch;}_str[n]='\0';}size=n;
}//插入字符版本
string& insert(size_t pos, char ch)
{assert(pos<=size);int len=size+1;if(len>capacity)reserve(len);unsigned int end = size+1;//unsigned int end = size;//while (pos <= end) //头插有bug,遇到unsigned<=某数要小心,容易最大化//{// _str[end + 1] = _str[end];// end--;//}//\0直接被处理,无需再管\0while (pos < end) //有bug,遇到unsigned<=某数要小心,容易最大化,unsigned容易减到-1成最大的{str[end] = _str[end-1];end--;}_str[pos] = ch;size += 1;return *this;
}//插入字符串版本 本质上从挪一个位置到要挪n个位置的区别
string& insert(size_t pos, const char* str)
{assert(pos<=size);int len=size+len(str);if(len>capacity)reserve(len);unsigned int end = size+len(str);//\0直接被处理,无需再管\0while (pos+len-1 < end) //有bug,遇到unsigned<=某数要小心,容易最大化,unsigned容易减到-1成最大的{str[end] = _str[end-len(str)];end--;}_str[pos] = ch;size += 1;return *this;
}
append、+=、push_back直接复用:
string& operator+=(const char* str){insert(size,str);return *this;}string& operator+=(char ch){insert(size,ch);return *this;}void push_back(const char ch){insert(size,ch);}void append(const char* str){insert(size,ch);}
查改:
const char& operator[](int pos)const //一般情况不修改对象内容的,都用const修饰,既考虑了const对象调用,又考虑了非const调用,如果有特殊需要可以都实现,调用时会自动识别
{assert(pos<size);return *(_str+pos);
}char& operator[](int pos) //用引用返回可以直接修改
{assert(pos<size);return *(_str+pos);
}size_t find(char ch, int pos) const
{assert(pos<size);while(_str[pos]!=ch){if(pos>=size)break;pos++;}if(pos==size)return npos;elsereturn pos;
}char* find(const char* str, int pos)
{assert(pos<size);char* poss=strstr(_str+pos,str)return poss;
}const char* find(const char* str, int pos) const
{assert(pos<size);char* poss=strstr(_str+pos,str)return poss;
}
迭代器的实现
class _string
{typedef char* iterator;typedef const char* const_iterator;//string这种物理内存连续的可以这么定义指针//一定要定义const迭代器 要保证权限不会放大,以及后续一致性问题,不能const字符串的指针能修改 字符串,这就扯淡了iterator begin(){return _str;}const_iterator begin() const{return _str;}iterator end(){return _str+size;}const_iterator end() const{return _str+size;}
}//范围for就是用的迭代器,要想支持范围for遍历就必须定义迭代器begin(),end(),一个字符都不能差。
删:
void erase(size_t pos=npos, int n)
{assert(pos<size)if(pos+n>size)_str[pos]='\0';size-=n;else{int start=pos;int end=n+pos;while(start<=end){_str[start]=_str[start+n];start++;}size-=n;}}
重载流提取流插入运算符,在类外重载:
ostream& operator<<(ostream& out, const string& s)
{for(auto e: s)out<<e;return out;
}//流输入要考虑的问题就多了,要考虑效率问题,以及普通的in不处理换行符的问题。
istream& operator>>(stream& in, string& s)
{//引入内存池char tmp[128]={'\0'};ch=in.get();//可以接收换行符,空格符,如果只in>>,不会接收int i = 0;while(ch !=' ' and ch!='\n'){if(i==128)s+=tmp;i=0;memset(tmp,'\0',128);tmp[i++]=ch;ch=in.get();}s+=tmp;return in;
}
这么写,其实有问题,输入时之前的旧内存没有释放掉:
//流输入要考虑的问题就多了,要考虑效率问题,以及普通的in不处理换行符的问题。
istream& operator>>(stream& in, string& s)
{s= string();//清空内存//引入内存池char tmp[128]={'\0'};ch=in.get();//可以接收换行符,空格符,如果只in>>,不会接收int i = 0;while(ch !=' ' and ch!='\n'){if(i==128)s+=tmp;i=0;memset(tmp,'\0',128);tmp[i++]=ch;ch=in.get();}s+=tmp;return in;
}