从零开始实现 std::string:让你更深入地了解字符串的本质
文章目录
- 前言
- string类 的模拟实现
- 一,搭建框架
- 二,重载输入输出操作符 ‘<<’ ‘>>’
- 1. 重载操作符 ‘<<’
- 2.重载操作符 ‘>>’
- 且看方式一
- 来看方式二
- 三,实现构造函数
- 方式一
- 方式二
- 四,实现拷贝构造和重载赋值操作符
- 传统的写法
- 1.拷贝构造
- 2. 重载赋值操作符
- 现代写法
- 1.拷贝构造
- 2. 重载赋值操作符
- 五,实现string 类相关的函数
- 1. reserve函数
- 2.resize函数
- 3. insert函数
- 1.插入字符
- 2.插入字符串
- 4.push_back函数
- 5. append函数
- 6.操作符 += 重载
- 7.erase函数
- 8.clear函数
- 9.find函数
- 10.substr函数
- 11.操作符 > == >= <= < != 重载
- **源码:**
- 后记
前言
本文将讲述怎么模拟实现string类,有些同学可能会问了,我要实现这个有什么用?会用不就可以了吗?
你没有错,但是我们通过模拟实现string类可以帮助我们更加深入的了解字符串具体是怎么一回事?它的内部结构是怎么样的?如果以后我们写程序,碰到字符串某个地方报错,也能很快排查出问题哦~
🕺作者: 迷茫的启明星
专栏:《C++初阶》😘欢迎关注:👍点赞🙌收藏✍️留言
🏇码字不易,你的👍点赞🙌收藏❤️关注对我真的很重要,有问题可在评论区提出,感谢阅读!!!
持续更新中~
string类 的模拟实现
一,搭建框架
我们首先把string类大概样子搭建出来,它不外乎构造函数、拷贝函数,析构函数、还有一些基于C语言实现的方便我们操作字符串的函数,那么我们就来搭建一下,需要提醒的是:一些注意事项我会放在代码的注释中便于理解。
#pragma once
#include <cstring>
#include <iostream>
#include <cassert>
using namespace std;
namespace hxq
{
class string
{
public:
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{
return _str;//返回首字符地址
}
iterator end()
{
//字符串一个字符占一个字节
return _str + _size;
}
//当然也有const类型
const_iterator begin() const
{
return _str;//返回首字符地址
}
const_iterator end() const
{
//字符串一个字符占一个字节
return _str + _size;//返回尾巴的地址
}
const char* c_str() const
{
return _str;//返回字符串首地址
}
size_t size() const
{
return _size;//返回字符串长度
}
size_t capacity() const
{
return _capacity;//返回string对象空间大小
}
//字符串类(String)中的 operator[] 运算符重载函数,
//用于访问字符串中指定位置上的字符(元素),
//并返回该字符的引用。
const char& operator[](size_t pos) const
{
assert(pos < _size);
return _str[pos];
}
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
//析构函数,释放资源
~string()
{
delete[] _str;
_str = nullptr;
_size=_capacity=0;
}
//一般来说我们尽量把参数放在一起
private:
size_t _capacity;//实际空间
size_t _size;//字符串长度(不包括'\0')
char* _str;//首字符地址
public:
const static size_t npos=-1;
//特殊值
//使用const static 语法修饰
//npos是最后一个字符的位置
};
}
二,重载输入输出操作符 ‘<<’ ‘>>’
1. 重载操作符 ‘<<’
将字符逐个输到输出流中
ostream& operator<<(ostream& out, const string& s)
{
for (char i : s)
{
out << i;
}
return out;
}
2.重载操作符 ‘>>’
且看方式一
按照一般思路,我们设计一个循环读取字符直到遇到空格或者回车停止
于是就有了以下实现
istream& operator>>(istream& in ,string& s)
{
char ch;
ch=in.get();
s.reserve(128);//预开128个字符的空间
while(ch!=' '&&ch!='\n')
{
s+=ch;
ch=in.get();
}
return in;
}
但是在测试的时候出现这样的问题
当string对象有默认值时,输入的字符串会加在后面
就像这样:
结果:
不仅如此,当输入字符串很长,不断+=,频繁扩容,代码效率很低
来看方式二
我们开辟一个缓存字符数组来存储输入的字符串
达到条件就把缓存空间最后一个值赋值为‘\0’
再尾加缓存空间,也就是尾加字符串
istream& operator>>(istream& in,string& s)
{
s.clear();
char ch;
ch=in.get();
const size_t N = 32;
char buffer[N];
size_t i =0;
//如果没有遇到空格或者回车就一直读取到buffer缓存区中
while(ch != ' '&&ch != '\n')
{
buffer[i++] = ch;
//当它满了就让s尾加buffer
//注意看buffer经过处理也是一个字符串
if(i == N-1)
{
buffer[i]='\0';
s+=buffer;
i=0;
}
ch=in.get();
}
//读取buffer中剩余字符
buffer[i]='\0';
s+=buffer;
return in;
}
三,实现构造函数
如何构造一个string的对象呢?
得考虑到三个成员变量:
- _str 字符串的地址
- _size 字符串长度
- _capacity 字符串空间
我们需要将它们初始化
于是以下代码就出现了
string()
:_str(new char[1])//先预存一个'\0'的空间
,_size(0)
,_capacity(0)
{
_str[0]='\0';
}
有些同学就会想不能这样实现吗?
string()
:_str(nullptr)//它没有给\0 预留空间
,_size(0)
,_capacity(0)
{}
不能这样子,它无法初始化空对象
但是如果构造函数有参数,这就不适用了,还要重载一个有参的构造函数,我们为什么不实现一个默认参数为“\0”的构造函数呢?
方式一
//string(const char* str = "\0")//二者都可以
string(const char* str = "")//""相当于"\0"
:_str(new char[strlen(str)+1])
, _size(strlen(str))
, _capacity(strlen(str))
{
strcpy(_str, str);
}
但是我们会发现它的初始化的时候调用了三次strlen函数是不是看上去就很拉?
于是就有同学想这样了↓
string(const char* str = "")
:_size(strlen(str))
,_capacity(_size)
,_str(new char[_capacity+1])
{
strcpy(_str,str);
}
不能这样,初始化的顺序是按照成员变量的顺序依次进行的
_str => _capacity => _size
在初始化_str的时候,_capacity是随机值,就不可行
但是我们可以这样,把初始化放在里面,就不会受到它的限制
方式二
string(const char* str = "")
{
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
四,实现拷贝构造和重载赋值操作符
传统的写法
1.拷贝构造
s2(s1)
按照我们之前对拷贝构造的理解
我们需要将s1的’_str’,‘_size’,'_capacity’依次赋值给s2的成员变量
注意:capacity要加1,为‘\0’预留位置
实现:
string(const string& s)
:_str(new char[s._capacity+1])
,_size(s.size())
,_capacity(s._capacity)
{
strcpy(_str,s._str);
}
2. 重载赋值操作符
假如说是:s1=s3
思路:
- 新开辟一个空间tmp,将s3._str赋值给tmp
- 将s1的_str删除
- 将tmp的值给s1
string& operator=(const string&s)
{
if(this!=&s)//判断是否是自己给自己赋值
{
//新开一个空间来存s3的值
char* tmp = new char[s._capacity+1];//多开一个给'\0'
strcpy(tmp,s._str);
//把delete[] _str放在开空间后面,避免空间不足,会友好一些
delete[] _str;//删除原来s1的值
//将tmp的值给s1
_str = tmp;
_size = s._size;
_capacity = s._capacity;
}
return *this;
}
现代写法
1.拷贝构造
还有一种现代写法
这是一种有着资本主义或者说老板思维,让员工干活
具体怎么回事呢?
s2(s1)
我们可以新建一个string对象,并且以s1._str来初始化它
也就是说利用了前文的构造函数string(const char* str = “”)
再将”员工“的’_str’,‘_size’,'_capacity’与this的相交换
这样我们就可以借助这个”员工“来实现拷贝构造
需要注意的是,这里需要进行初始化为nullptr,0,0的操作
因为被调用的员工被处理(析构)的时候_str不能是随机空间(脏数据)(感兴趣的同学可以试着自己试试)
string(const string&s)
:_str(nullptr)
,_size(0)
,_capacity(0)
{
string tmp(s._str);
swap(_str,tmp._str);
swap(_size,tmp._size);
swap(_capacity,tmp._capacity);
}
但是它似乎有点不太美观,我们将他放到一个函数中
void swap(string& tmp)//这是我们定义的函数
{
//↓这是库里面的函数,我们以后再讲
::swap(_str,tmp._str);
::swap(_size,tmp._size);
::swap(_capacity,tmp._capacity);
}
string(const string&s)
:_str(nullptr)
,_size(0)
,_capacity(0)
{
string tmp(s._str);
swap(tmp);
//相当于this->swap(tmp)
}
2. 重载赋值操作符
再想想,刚刚的赋值操作符是不是也有相似之处呢?
我们只要和拷贝构造那样”雇佣“一个”员工“帮助我们实现即可
可以让代码非常简洁,就像这个式子:s1=s2
我们不需要考虑后面那个值的下场,因为它只是走个过场
所以直接调用swap函数交换它们的’_str’,‘_size’,‘_capacity’
string& operator=(string s)
{
swap(s);
return *this;
}
五,实现string 类相关的函数
1. reserve函数
在 C++ 的 string 类中,reserve() 是一个成员函数,
它用于在字符串中预分配一定的内存空间,以提高字符串操作的效率。
具体来说,reserve() 函数的功能是控制字符串对象的容量(capacity),
也就是分配给字符串对象的内存空间大小。当字符串对象的长度超过容量时,
会导致再次分配内存空间,从而影响性能。
因此,通过使用 reserve() 函数可以提前预留一定的内存空间,
以避免频繁地重新分配内存,从而提高程序的运行效率。
实现思路:
- 如果需要分配的值大于字符串的_capacity就申请空间tmp
- 将原来的字符串按字节拷贝给tmp
- 删除原来的字符串的值
- 将tmp的地址给_str
代码:
void reserve(size_t n)
{
if(n > _capacity)
{
char* tmp=new char[n+1];
strcpy(tmp,_str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
2.resize函数
在 C++ 的 string 类中,resize() 是一个成员函数,
它用于重新设置字符串的长度(size),并可以选择是否填充新添加的字符。
具体来说,resize() 函数的功能是控制字符串对象的长度和内容。
当需要改变字符串对象的长度时,可以使用 resize() 函数来进行操作。
如果新的长度小于或等于原长度,则会删除超出部分的字符;
如果新的长度大于原长度,则会在末尾添加新的字符。
实现思路:
- 分为两种情况
- 如果新的长度大于原长度就申请空间再将新的字符添加即可
- 如果新的长度小于或等于原长度,则会删除超出部分的字符
代码:
void resize(size_t n, char ch = '\0')
{
if (n > _size)
{
reserve(n);
for (size_t i = _size; i < n; ++i) {
_str[i] = ch;
}
_str[n]='\0';
_size = n;
}
else
{
_str[n]='\0';
_size = n;
}
}
3. insert函数
在 C++ 的 string 类中,
insert()
是一个成员函数,它用于将一个字符串或字符序列插入到另一个字符串的指定位置。
具体来说,
insert()
函数的功能是在字符串对象的指定位置插入一个字符串或字符序列。可以通过传递不同的参数组合来选择需要插入的内容和插入位置。
在这里我们仅实现两种常用的
1.插入字符
实现思路:
insert(size_t pos,char ch)
- pos>字符串长度报错
- 如果空间不够扩容
- 将pos位置后面的字符往后挪
代码:
//使用引用,提高工作效率
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;
}
_str[pos] = ch;
++_size;
return *this;
}
2.插入字符串
实现思路:
insert(size_t pos,const char* str)
- 和插入单个字符类似
- pos>字符串长度报错
- 如果空间不够扩容
- 将pos位置后面的字符往后挪
- 注意挪的长度是插入字符串的长度
代码:
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;
}
strncpy(_str + pos,str,len);
_size += len;
return *this;
}
4.push_back函数
尾插
实现思路:
push_back(char ch)
- 方式一:判断空间是否足够,在最后设置需要增加字符,再在其后加上‘\0’即可
- 方式二可以直接使用前面的insert函数进行尾插即可
代码:
//方式一
void push_back(char ch)
{
if(_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
_str[_size]=ch;
++_size;
_str[_size] = '\0';
}
//方式二
void push_back(char ch)
{
insert(_size,ch);
}
5. append函数
用来将一个字符串追加到另一个字符串的末尾,使其成为一个更长的字符串。
实现思路:
append(const char*str)
- 方式一:判断原字符串空间是否足够,直接将追加的字符串拷贝在它后面
- 方式二:使用前面的insert函数尾插字符串
代码:
//方式一
void append(const char*str)
{
//方式一
size_t len = strlen(str);
if (_size+len>_capacity)
{
reserve(_size+len);
}
strcpy(_str+_size,str);
//还有一个函数是strcat(_str,str);
//但是思考一下它的实现方式就会发现它首先要找'\0',那么就不高效
_size+=len;
}
//方式二
void append(const char*str)
{
insert(_size,str);
}
6.操作符 += 重载
相当于尾插字符和尾插字符串
实现思路:
string& operator+=(char ch)
- 尾插字符利用push_back函数
string& operator+=(const char* str)
- 尾插字符串利用append函数
代码:
//加字符
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
//加字符串
string& operator+=(const char* str)
{
append(str);
return *this;
}
7.erase函数
从pos开始删除长度为len的字符串,默认npos即为删到尾
实现思路:
erase(size_t pos , size_t len = npos)
- pos>字符串长度报错
- 如果是最后一个或者大于字符串的长度,那么就在pos这个位置上设为’\0’
- 否则将位置[pos+len]后面的字符串给到[pos]位置,复制结束时再在尾巴上给一个’\0’
代码:
void erase(size_t pos , size_t len = npos)
{
assert(pos<_size);
//如果是最后一个或者大于字符串的长度,那么就在pos这个位置上设为'\0'
if(len == npos || pos + len >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
//将位置[pos+len]后面的字符串给到[pos]位置
//复制结束时再在尾巴上给一个'\0'
strcpy(_str + pos,_str+pos+len);
_size -=len;
}
}
8.clear函数
字符清空,长度置为0
实现思路:
clear()
- 利用字符串以‘\0’结尾的性质
- 直接将字符串第一个位置置为‘\0’
- 再将长度置为0
代码:
void clear()
{
_str[0] = '\0';
_size = 0;
}
9.find函数
从pos位置开始寻找返回子串在字符串中第一次出现的位置,如果没有找到,则返回
std::string::npos
实现思路:
//找某个字符 size_t find(char ch,size_t pos = 0) const
- 遍历字符串,寻找匹配字符
//找匹配的字符串 size_t find(const char* sub,size_t pos = 0)const
- 利用C语言库中的strstr函数
- strstr 函数返回的指针指向的是源字符串中第一次出现子串的位置
代码:
//找某个字符
size_t find(char ch,size_t pos = 0) const
{
assert(pos<_size);
for(size_t i= pos;i<_size;++i)
{
if(ch == _str[i])
{
return i;
}
}
return npos;
}
//找匹配的字符串
size_t find(const char* sub,size_t pos = 0)const
{
assert(sub);
assert(pos<_size);
//strstr 函数返回的指针指向的是源字符串中第一次出现子串的位置
const char* ptr = strstr(_str+pos,sub);
if(ptr == nullptr)
{
return npos;
}
else
{
return ptr-_str;
}
}
10.substr函数
用于从字符串中提取子串
实现思路:
string substr(size_t pos,size_t len = npos) const
- 新建一个string对象
- 从pos位置开始,截取长度为len的字符串
代码:
string substr(size_t pos,size_t len = npos) const
{
assert(pos<_size);
size_t realLen = len;
if (len == npos || pos+len>_size)
{
realLen = _size - pos;
}
string sub;
for (size_t i = 0; i < realLen; ++i) {
sub+=_str[pos+i];
}
return sub;
}
11.操作符 > == >= <= < != 重载
实现思路:
利用strcmp函数判断大小
代码:
bool operator>(const string&s)const
{
return strcmp(_str,s._str)>0;
}
bool operator==(const string& s) const
{
return strcmp(_str, s._str) == 0;
}
bool operator>=(const string& s) const
{
return *this>s||*this == s;
}
bool operator<=(const string& s) const
{
return !(*this>s);
}
bool operator<(const string& s) const
{
return !(*this >= s);
}
bool operator!=(const string& s) const
{
return !(*this == s);
}
到此我们就模拟了string类的大部分功能,还有一些都是相关的函数,我就不再过多赘述了,这里面最重要的就是构造函数和拷贝构造,一定要理解清楚!
源码:
namespace hxq
{
class string
{
//框架
public:
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{
return _str;//返回首字符地址
}
iterator end()
{
return _str + _size; //字符串一个字符占一个字节
}
//当然也有const类型
const_iterator begin() const
{
return _str;//返回首字符地址
}
const_iterator end() const
{
return _str + _size; //字符串一个字符占一个字节
}
string(const char* str = "")
{
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
//拷贝构造
//s2(s1)
//传统写法
// string(const string& s)
// :_str(new char[s._capacity+1])
// ,_size(s.size())
// ,_capacity(s._capacity)
// {
// strcpy(_str,s._str);
// }
// //假设说是这样的式子:s1=s3
// string& operator=(const string&s)
// {
// if(this!=&s)//判断是否是自己给自己赋值
// {
// //新开一个空间来存s3的值
// char* tmp = new char[s._capacity+1];//多开一个给'\0'
// strcpy(tmp,s._str);
// //把delete[] _str放在开空间后面,避免空间不足,会友好一些
// delete[] _str;//删除原来s1的值
// //将tmp的值给s1
// _str = tmp;
// _size = s._size;
// _capacity = s._capacity;
// }
// return *this;
// }
// string(const string&s)
// :_str(nullptr)
// ,_size(0)
// ,_capacity(0)
// {
// string tmp(s._str);
// swap(_str,tmp._str);
// swap(_size,tmp._size);
// swap(_capacity,tmp._capacity);
// }
//但是它似乎有点不太美观,我们将他放到一个函数中
//再简化
void swap(string& tmp)//这是我们定义的函数
{
//↓这是库里面的函数,我们以后再讲
::swap(_str,tmp._str);
::swap(_size,tmp._size);
::swap(_capacity,tmp._capacity);
}
string(const string&s)
:_str(nullptr)
,_size(0)
,_capacity(0)
{
string tmp(s._str);
swap(tmp);
//相当于this->swap(tmp)
}
string& operator=(string s)
{
swap(s);
return *this;
}
const char* c_str() const
{
return _str;
}
//一些函数
//分配空间
void reserve(size_t n)
{
if(n > _capacity)
{
char* tmp=new char[n+1];
strcpy(tmp,_str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
//重新设置长度,后面参数如果有的话就是加n个ch
void resize(size_t n, char ch = '\0')
{
if (n > _size)
{
reserve(n);
for (size_t i = _size; i < n; ++i) {
_str[i] = ch;
}
_str[n]='\0';
_size = n;
}
else
{
_str[n]='\0';
_size = n;
}
}
//插入字符
//使用引用,提高工作效率
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;
}
_str[pos] = ch;
++_size;
return *this;
}
//插入字符串
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;
}
strncpy(_str + pos,str,len);
_size += len;
return *this;
}
void push_back(char ch)
{
//方式一
// if(_size == _capacity)
// {
// reserve(_capacity == 0 ? 4 : _capacity * 2);
// }
//
// _str[_size]=ch;
// ++_size;
// _str[_size] = '\0';
//方式二
insert(_size,ch);
}
void append(const char*str)
{
//方式一
size_t len = strlen(str);
if (_size+len>_capacity)
{
reserve(_size+len);
}
strcpy(_str+_size,str);
//strcat(_str,str);
//思考一下它的实现方式就会发现它首先要找'\0',那么就不高效
_size+=len;
//方式二
insert(_size,str);
}
//加字符
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
//加字符串
string& operator+=(const char* str)
{
append(str);
return *this;
}
//删除
//从pos开始删除长度为len的字符串,默认npos即为删到尾
void erase(size_t pos , size_t len = npos)
{
assert(pos<_size);
//如果是最后一个或者大于字符串的长度,那么就在pos这个位置上设为'\0'
if(len == npos || pos + len >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
// 将位置[pos+len]后面的字符串给到[pos]位置
// 复制结束时再在尾巴上给一个'\0'
strcpy(_str + pos,_str+pos+len);
_size -=len;
}
}
//想到字符串是以'\0'判断结束的,于是_str[0] = '\0',再把_size设为0结束
void clear()
{
_str[0] = '\0';
_size = 0;
}
//找某个字符
size_t find(char ch,size_t pos = 0) const
{
assert(pos<_size);
for(size_t i= pos;i<_size;++i)
{
if(ch == _str[i])
{
return i;
}
}
return npos;
}
//找匹配的字符串
size_t find(const char* sub,size_t pos = 0)const
{
assert(sub);
assert(pos<_size);
//strstr 函数返回的指针指向的是源字符串中第一次出现子串的位置
const char* ptr = strstr(_str+pos,sub);
if(ptr == nullptr)
{
return npos;
}
else
{
return ptr-_str;
}
}
//截取字符串
string substr(size_t pos,size_t len = npos) const
{
assert(pos<_size);
size_t realLen = len;
if (len == npos || pos+len>_size)
{
realLen = _size - pos;
}
string sub;
for (size_t i = 0; i < realLen; ++i) {
sub+=_str[pos+i];
}
return sub;
}
bool operator>(const string&s)const
{
return strcmp(_str,s._str)>0;
}
bool operator==(const string& s) const
{
return strcmp(_str, s._str) == 0;
}
bool operator>=(const string& s) const
{
return *this>s||*this == s;
}
bool operator<=(const string& s) const
{
return !(*this>s);
}
bool operator<(const string& s) const
{
return !(*this >= s);
}
bool operator!=(const string& s) const
{
return !(*this == s);
}
//框架
size_t size() const
{
return _size;
}
size_t capacity() const
{
return _capacity;
}
const char& operator[](size_t pos) const
{
assert(pos < _size);
return _str[pos];
}
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
~string()
{
delete[] _str;
_str = nullptr;
_size=_capacity=0;
}
//一般来说我们尽量把参数放在一起
private:
size_t _capacity;//实际空间
size_t _size;//字符串长度(不包括'\0')
char* _str;//首字符地址
public:
const static size_t npos=-1;
//特殊值
//使用const static 语法修饰
//npos是最后一个字符的位置
};
//重载操作符<< 按照字符逐字节输到输出流中
ostream& operator<<(ostream& out, const string& s)
{
for (char i : s)
{
out << i;
}
return out;
}
//方式一:输入字符串很长,不断+=,频繁扩容,效率很低
//而且会出现这样的情况,当string对象有默认值时,输入的字符串会加在后面
// istream& operator>>(istream& in ,string& s)
// {
// char ch;
// ch=in.get();
// s.reserve(128);
// while(ch!=' '&&ch!='\n')
// {
// s+=ch;
// ch=in.get();
// }
// return in;
// }
//方式二
//设计一个缓存数组
istream& operator>>(istream& in,string& s)
{
s.clear();
char ch;
ch=in.get();
const size_t N = 32;
char buffer[N];
size_t i =0;
//如果没有遇到空格或者回车就一直读取到buffer缓存区中
while(ch != ' '&&ch != '\n')
{
buffer[i++] = ch;
//当它满了就让s尾加buffer
//注意看buffer经过处理也是一个字符串
if(i == N-1)
{
buffer[i]='\0';
s+=buffer;
i=0;
}
ch=in.get();
}
//读取buffer中剩余字符
buffer[i]='\0';
s+=buffer;
return in;
}
}
后记
感谢大家支持!!!
respect!
下篇见!