模拟实现string,首先我们要知道成员变量有哪些:
cpp
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中,初始化方式有无参的,有用字符串初始化的,这就要求我们写一个带缺省参数的默认构造函数:
cpp
string(const char* str="")
:capacity(strlen(str)),
size(capacity)
{
_str=new char[capacity+1];
strcpy(_str,str);
}
//为什么用"",因为它代表了\0,用它初始化再合适不过了
//为什么不用'\0'呢?因为这是一个char*,用'\0'就会用他的ASCII码值初始化
//为什么不用nullptr呢?因为在后面可能会用到_str的解引用,解引用空指针要出问题的
接下来写拷贝构造
cpp
string(const string& s)
{
_str=new char[s.capacity+1];
capacity=s.capacity;
size=s.size;
strcpy(_str,s._str);
}
赋值符号重载
cpp
//与单纯的拷贝构造不一样,复制重载原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对象的字符串呢?在没有重载流提取符号的情况下:
cpp
const char* c_str() const
{
return _str;
}
开始定义增删查改部分;增:有push_back、append、+=、insert,直接先定义insert,其他的都可以复用insert函数,insert有插入字符版本和字符串版本:
cpp
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直接被处理,无需再管\0
while (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直接被处理,无需再管\0
while (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直接复用:
cpp
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);
}
查改:
cpp
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;
else
return 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;
}
迭代器的实现
cpp
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(),一个字符都不能差。
删:
cpp
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;
}
}
重载流提取流插入运算符,在类外重载:
cpp
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;
}
这么写,其实有问题,输入时之前的旧内存没有释放掉:
cpp
//流输入要考虑的问题就多了,要考虑效率问题,以及普通的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;
}