文章目录
- 前言
- 成员变量
- 成员函数
- 构造函数
- 浅拷贝
- 深拷贝
- 构造函数实现
- 析构函数
- 赋值重载
- 迭代器
- begin & end
- rbegin & rend
- 空间管理函数
- 元素访问
- 元素修改
- 字符串运算
- 流提取 & 流插入
- 流提取
- 流插入
- 总结
前言
模拟实现不是为了写得和库里面一样好。而是为了更好的了解底层,从而能够更熟练的使用这些类,同时也能学习大佬们的代码风格。
在学习这章之前,需要对类与对象有一定的知识基础,如果对类与对象有些生疏的话,可以看看这篇文章:《类与对象》
string 类是 C++标准库中的一个重要类,用于表示字符串。
以下是一些关于 string 类的主要特点:
- 动态存储:可以自动管理内存,根据字符串的实际长度动态分配和释放内存。
- 丰富的操作:提供了很多方便的操作方法,如字符串连接、查找、比较、提取子串等。
- 高效性:在性能上表现较好,适用于各种字符串处理场景。
成员变量
在正式模拟实现之前,我们得要先确定我们要实现的类中的成员变量由哪些类型构成:
- 首先需要有一个字符指针,用于存储字符串。
- 再定义一个变量,用来记录字符串的有效个数。
- 再定义一个变量,用来记录该字符指针能存储的有效字符个数大小
- 再定义一个全局变量
npos
,来完成以下操作:npos
是一个静态成员常量值,具有size_t
类型元素的最大可能值。- 当在
string
的成员函数中作为len
(或sublen
)参数的值时,该值表示“直到字符串结束”。 - 作为返回值,它通常用于表示没有匹配。
- 该常量定义为
-1
,由于size_t
是无符号整型,因此它是该类型的最大可表示值。
class string
{
private:
char* _str = nullptr; // 字符指针, 字符串用于存储字符串
size_t _size = 0; // 用于记录该字符串有几个有效字符(即字符串的长度)
size_t _capacity = 0; // 用于记录该字符指针能存储的有效字符个数大小
// Member constants:
const static size_t npos = -1;
};
成员函数
构造函数
空字符串构造函数(默认构造函数)
构造一个长度为0个字符的空字符串。
string(const char* str = "")
{
size_t size = strlen(str);
_size = _capacity = size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
拷贝构造函数
这里需要先介绍两个概念:
浅拷贝
浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规。
就像一个家庭中有两个孩子,但父母只买了一份玩具,两个孩子愿意一块玩,则万事大吉,万一不想分享就你争我夺,玩具损坏。
深拷贝
可以采用深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不要和其他对象共享。父母给每个孩
子都买一份玩具,各自玩各自的就不会有问题了。
构造函数实现
构造一个str的副本,这里因为涉及到资源管理的问题,所以拷贝构造时,我们需要重新创建一个空间,来存储str的内容。否则如果直接浅拷贝的话,会导致一下两个问题:
- 对其中一个字符串改变时,另一个字符串也会跟着被改变(因为它们管理的是同一片空间)
- 在调用析构函数时,同一份资源被释放两次
string(const string& str)
{
_str = new char[str._capacity + 1];
strcpy(_str, str._str);
_size = str._size;
_capacity = str._capacity;
}
string
构造函数不止有这两个,如果想要去实现其他的,也不是很困难。只是这两个构造函数,在大部分的情况下就够用了。
析构函数
对于这些参与空间资源分配的类,我们都要自己写析构函数,否则很可能导致内存泄漏。
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
赋值重载
为字符串赋一个新值,替换其当前内容(如果是两个相等的字符串就没必要相互赋值了)。
string& operator=(const string& str)
{
if (str != *this)
{
delete[] _str;
_str = new char[str._capacity + 1];
strcpy(_str, str._str);
_size = str._size;
_capacity = str._capacity;
}
return *this;
}
这里还有一种写法,我们可以复用拷贝构造函数:
string& operator=(const string& str)
{
if (str != this->_str)
{
string tmp = str;
swap(tmp);
}
return *this;
}
当然这里的swap函数是需要我们自己实现的:
void swap(string& str)
{
std::swap(_str, str._str);
std::swap(_size, str._size);
std::swap(_capacity, str._capacity);
}
这里我复用了std空间里面的swap函数,将string类中的成员变量都交换掉。
迭代器
begin & end
这里只模拟实现我标记出来的那些,C++11出的那几个迭代器,主要是为了区分普通迭代器(iterator)和常量迭代器(const_iterator),但其实,我们用上面的那些就能够涵盖普通迭代器与常量迭代器了(因为普通迭代器和常量迭代器是可以重载的)
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
的底层空间是连续的,所以我们可以直接使用原生指针来定义为迭代器。
当然,使用范围for的前提是需要满足满足迭代器begin
和end
,才能满足范围for
这里我只是将begin
改为Begin
,就不能支持范围for了
rbegin & rend
再说反向迭代器之前,需要具备适配器模式的知识,如果不是很了解可以先看看这篇文章:适配器模式
解释一下为什么要具有适配器模式的知识再往下看:
如果我们只是单纯的完成string类的反向迭代器的功能,其实就可以直接复制一遍正向迭代器的代码,改一下就能实现这个功能了。
但STL中有很多容器,每个容器的底层实现都不一样,所以C++中的迭代器主要有以下几种类型:
- 输入迭代器:只读,只能顺序向前移动。
- 输出迭代器:只写,只能顺序向前移动。
- 前向迭代器:可读写,能顺序向前移动。
- 双向迭代器:可读写,能顺序向前和向后移动。
- 随机访问迭代器:可读写,能跳跃式地访问元素。
有了适配器模式,我们写一份代码。就能让与该迭代器模式相似的容器中的反向迭代器复用这里的代码(比如,string
和vector
的迭代器都是支持随机访问的,所以我们写一份能够支持随机访问的反向迭代器的代码,就能使string
和vector
都能够复用)
这里使用了三个模板参数,目的如下:
在使用迭代器时,我们难免会使用->
(结构体指针访问成员)操作符,或者使用*
(解引用)操作符(关于这两个操作符的重载,在模拟实现list章节会重点讲,因为在string
和vector
的底层物理结构是连续的空间,我们用原生指针就能当做它的迭代器,所以不需要重载这两个操作符。但是我们在这里实现的反向迭代器不是原生指针,而是用一个结构体封装起来的结构,从上面实现可以看出。所以需要重载这两个操作符。这里只要先知道,重载->
操作符的返回值是char*
或者const char*
,重载*
操作符的返回值是char&
或者const char&
)。
因为 const char&
是只读的,而 char&
可读可写,同理char*
可以改变所指向的内容,const char*
不能改变所指向的内容。所以我们需要将两个迭代器区分开来,而我们嵌套三个模板参数,就可以用后面两个模板参数来区分了。这样,我们就只用写一份代码,const和非const的迭代器就都能使用这一份代码了。我在这里用一个图给大家展示一下:
但是请注意:在string中,我们并不会用到->
操作符,因为->
操作符是自定义类型的指针对其成员的一个访问操作符,string类不会去存一个自定义类型,因为在后面模拟实现vector类的时候,反向迭代器我也会使用这段代码,所以这里我设计成三个模板参数。(实际上char&
或const char&
是不用传递过来的,但为了能够使用这串代码传过来也一样的)
再来看看实现部分,这里我是模拟库里面实现了一个对称的结构:
所以我们在对迭代器进行解引用操作时,是对当前位置的前一个位置解引用(此时迭代器的位置不发生改变,所以我们需要创建一个迭代器用于记录当前位置的前一个位置,再对该位置解引用)。
而对于前置++/–,和后置++/–的实现,是利用正向迭代器向相反方向移动实现的:
具体一些操作实现如下:
template<class Iterator, class Reference, class Point>
struct __random_reverse_iterator
{
typedef __random_reverse_iterator self;
__random_reverse_iterator(const Iterator& it)
:_cur(it)
{}
Reference operator*()
{
Iterator tmp = _cur;
return *(--tmp);
}
Point operator->()
{
return &(this->operator*());
}
self& operator++()
{
--_cur;
return *this;
}
self operator++(int)
{
self tmp = *this;
--_cur;
return tmp;
}
self& operator--()
{
++_cur;
return *this;
}
self operator--(int)
{
self tmp = *this;
++_cur;
return tmp;
}
self& operator+=(int nums)
{
_cur -= nums;
return *this;
}
self& operator-=(int nums)
{
_cur += nums;
return *this;
}
bool operator!=(const self& it)
{
return _cur != it._cur;
}
bool operator==(const self& it)
{
return _cur == it._cur;
}
private:
Iterator _cur;
};
// 下面是我们实现的string类(只含反向迭代器部分)
class string
{
public:
typedef __random_reverse_iterator<iterator, char&, char*> reverse_iterator;
typedef __random_reverse_iterator<const_iterator, const char&, const char*> const_reverse_iterator;
reverse_iterator rbegin()
{
return reverse_iterator(end());
}
reverse_iterator rend()
{
return reverse_iterator(begin());
}
const_reverse_iterator rbegin() const
{
return const_reverse_iterator(end());
}
const_reverse_iterator rend() const
{
return const_reverse_iterator(begin());
}
};
这里我并没有将这个反向迭代器的所用功能实现完,但是基本的框架也是有了。
空间管理函数
这里只模拟实现我勾选的这些函数。
实现这些功能并不复杂,代码如下:
size_t capacity() const
{
return _capacity;
}
size_t size() const
{
return _size;
}
void reserve(size_t n = 0)
{
assert(_str);
// 我实现的是只考虑扩容的,不考虑缩容
if (_capacity < n)
{
char* tmp = new char[n + 1]; // 开辟空间
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
void resize(size_t n, char c = '\0')
{
assert(_str);
// 不考虑缩容的情况
if (n > _capacity)
{
reserve(n);
for (size_t i = _size;i < n;++i)
_str[i] = c;
_str[_size = n] = '\0';
}
}
void clear()
{
_str[_size = 0] = '\0';
}
bool empty() const
{
return _size == 0;
}
元素访问
要实现[]
重载并不复杂,但是需要写两个,一个是可读可写的,一个是只读的:
char& operator[](size_t n)
{
assert(n >= 0);
assert(n < _size);
return _str[n];
}
const char& operator[](size_t n) const
{
assert(n >= 0);
assert(n < _size);
return _str[n];
}
元素修改
这里有些函数我是重载了几个版本的,具体实现细节我写进注释里了
void push_back(char c)
{
assert(_str);
// 先判断是否需要扩容
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 10 : 2 * _capacity;
reserve(newcapacity);
}
_str[_size] = c;
_str[++_size] = '\0'; // 增加字符以后不要忘记将_size增加
}
string& append(const string& str)
{
assert(_str && str._str);
// 先判断是否需要扩容
size_t len = strlen(str._str);
if (_size + len >= _capacity)
{
size_t newcapacity = _size + len + 10; // 每次扩容都多增加是个容积的容错
reserve(newcapacity);
}
memmove(_str + _size, str._str, len);
_str[_size += len] = '\0';
return *this;
}
string& operator+=(char c)
{
push_back(c);
return *this;
}
string& operator+=(const string& str)
{
append(str);
return *this;
}
string& insert(size_t pos, const string& str)
{
assert(pos <= _size);
size_t len = strlen(str._str);
if (_size + len >= _capacity)
reserve(_size + len);
size_t end = _size + len;
while (end - len + 1 > pos)
{
_str[end] = _str[end - len];
--end;
}
memmove(_str + pos, str._str, len);
_size += len;
return *this;
}
string& insert(size_t pos, char c)
{
assert(pos <= _size);
if (_size >= _capacity)
{
size_t newcapacity = _capacity == 0 ? 10 : 2 * _capacity;
reserve(newcapacity);
}
size_t end = _size + 1;
while (end > pos)
{
_str[end] = _str[end - 1];
--end;
}
_str[pos] = c;
++_size;
return *this;
}
string& erase(size_t pos = 0, size_t len = npos)
{
assert(pos < _size);
if (len == npos || pos + len >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
size_t end = pos + len;
while (end <= _size)
{
_str[end - len] = _str[end];
++end;
}
_size -= len;
}
return *this;
}
void swap(string& str)
{
std::swap(_str, str._str);
std::swap(_size, str._size);
std::swap(_capacity, str._capacity);
}
字符串运算
c_str
和data
的功能都是一样的。这两个函数是为了能够让string类兼容C语言中的一些函数。比如:我们使用C语言中的fopen
函数打开一个文件名(文件名是一个常量字符串),而假设我们使用string
类来管理文件名,这是,就需要这两个函数来将string
类转换成常量字符串了。
const char* c_str()
{
return _str;
}
const char* data()
{
return _str;
}
size_t find(const string& str, size_t pos = 0) const
{
char* tmp = strstr(_str + pos, str._str);
if (tmp == nullptr) // 查找子串失败
return npos;
return tmp - _str; // 返回两个指针之间的间隔距离,即表示找到的子串的起始位置
}
size_t find(char c, size_t pos = 0) const
{
for (size_t i = 0;i < _size;++i)
if (_str[i] == c) return i;
return npos;
}
size_t rfind(char c, size_t pos = 0) const
{
for (size_t i = _size - 1;i != npos;--i)
if (_str[i] == c) return i;
return npos;
}
string substr(size_t pos = 0, size_t len = npos) const
{
assert(pos < _size);
string str;
if (len == npos || pos + len >= _size)
len = _size;
for (size_t i = pos;i < len;++i)
str += _str[i];
return str;
}
流提取 & 流插入
流提取
因为在STL中string
类的对象是可以直接使用cout
打印出来的,如果我们也想让我们自己实现的string
能够直接打印出来,就需要重载一下<<
操作符,具体操作如下:
std::ostream& operator<<(std::ostream& out, const string& str)
{
for (auto& e : str)
out << e;
return out;
}
流插入
STL中string
类也是支持cin
插入数据的,如果我们也想让我们自己实现的string
能够使用cin
插入数据,就需要重载一下>>
操作符,具体操作如下:
std::istream& operator>>(std::istream& in, string& str)
{
str.clear();
char ch = in.get();
while (ch != ' ' && ch != '\n')
{
str += ch;
ch = in.get();
}
return in;
}
我们需要先将原string
对象中的资源清理一下,再插入。这里是利用了istream
类中的get()
函数从输入缓冲区获取我们输入的字符,然后再使用我们在上面重载的+=
操作符,来将我们输入的一组数据插入到一个string
对象中。
但这里存在一个问题:
就是如果我们每次读取一个字符,再用+=
这个操作符时,假设输入缓冲区中的数据量很大,那么必定存在很多次扩容,扩容还是会有一定的消耗的,所以我们使用一个字符数组来充当缓冲区,现将字符读入到这个数组中,待数组满了以后,我们再将这个数组中的数据插入到string
对象中,这样就能有效的降低扩容的次数了
std::istream& operator>>(std::istream& in, string& str)
{
str.clear();
char buffer[128] = {};
int count = 0;
for (char ch = in.get();ch != ' ' && ch != '\n';ch = in.get())
{
buffer[count++] = ch;
if (count == 127)
{
str += buffer;
count = 0;
}
}
if (count > 0)
{
buffer[count] = '\0';
str += buffer;
}
return in;
}
这里还需要重载一个函数getline()
获取一行字符串。因为>>
操作符,遇到空格或者回车就结束读取了,如果我们需要读取一行字符串(里面可能含有空格),这是就需要使用getline()
了
std::istream& getline(std::istream& in, string& str)
{
str.clear();
char buffer[128] = {};
int count = 0;
for (char ch = in.get();ch != '\n';ch = in.get())
{
buffer[count++] = ch;
if (count == 127)
{
str += buffer;
count = 0;
}
}
if (count > 0)
{
buffer[count] = '\0';
str += buffer;
}
return in;
}
总结
我在这里将我模拟实现string类的源码附在下面,供大家参考一下如何封装的(因为是在学习阶段,所以封装做得可能不是太好,以下只是我个人的理解):
#include <cstring>
#include <cassert>
namespace hyt
{
template<class Iterator, class Reference, class Point>
struct __random_reverse_iterator
{
typedef __random_reverse_iterator self;
__random_reverse_iterator(const Iterator& it)
:_cur(it)
{}
Reference operator*()
{
Iterator tmp = _cur;
return *(--tmp);
}
Point operator->()
{
return &(this->operator*());
}
self& operator++()
{
--_cur;
return *this;
}
self operator++(int)
{
self tmp = *this;
--_cur;
return tmp;
}
self& operator--()
{
++_cur;
return *this;
}
self operator--(int)
{
self tmp = *this;
++_cur;
return tmp;
}
self& operator+=(int nums)
{
_cur -= nums;
return *this;
}
self& operator-=(int nums)
{
_cur += nums;
return *this;
}
bool operator!=(const self& it)
{
return _cur != it._cur;
}
bool operator==(const self& it)
{
return _cur == it._cur;
}
private:
Iterator _cur;
};
class string
{
public: // 声明友元函数
friend bool operator==(const string& lhs, const string& rhs);
friend bool operator!=(const string& lhs, const string& rhs);
friend bool operator>(const string& lhs, const string& rhs);
friend bool operator>=(const string& lhs, const string& rhs);
friend bool operator<(const string& lhs, const string& rhs);
friend bool operator<=(const string& lhs, const string& rhs);
public: // 迭代器
typedef char* iterator;
typedef const char* const_iterator;
typedef __random_reverse_iterator<iterator, char&, char*> reverse_iterator;
typedef __random_reverse_iterator<const_iterator, const char&, const char*> const_reverse_iterator;
// 普通迭代器
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
// const迭代器
const_iterator begin() const
{
return _str;
}
const_iterator end() const
{
return _str + _size;
}
// 反向普通迭代器
reverse_iterator rbegin()
{
return reverse_iterator(end());
}
reverse_iterator rend()
{
return reverse_iterator(begin());
}
// 反向const迭代器
const_reverse_iterator rbegin() const
{
return const_reverse_iterator(end());
}
const_reverse_iterator rend() const
{
return const_reverse_iterator(begin());
}
public:
// 构造函数
string(const char* str = "")
{
size_t size = strlen(str);
_size = _capacity = size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
// 拷贝构造
string(const string& str)
{
_str = new char[str._capacity + 1];
strcpy(_str, str._str);
_size = str._size;
_capacity = str._capacity;
}
// 赋值操作符重载
string& operator=(const string& str)
{
if (str != this->_str)
{
string tmp = str;
swap(tmp);
}
return *this;
}
// 析构
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
// Capacity:
size_t capacity() const
{
return _capacity;
}
size_t size() const
{
return _size;
}
void reserve(size_t n = 0)
{
assert(_str);
// 我实现的是只考虑扩容的,不考虑缩容
if (_capacity < n)
{
char* tmp = new char[n + 1]; // 开辟空间
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
void resize(size_t n, char c = '\0')
{
assert(_str);
// 不考虑缩容的情况
if (n > _capacity)
{
reserve(n);
for (size_t i = _size;i < n;++i)
_str[i] = c;
_str[_size = n] = '\0';
}
}
void clear()
{
_str[_size = 0] = '\0';
}
bool empty() const
{
return _size == 0;
}
// String operations:
const char* c_str()
{
return _str;
}
const char* data()
{
return _str;
}
size_t find(const string& str, size_t pos = 0) const
{
char* tmp = strstr(_str + pos, str._str);
if (tmp == nullptr) // 查找子串失败
return npos;
return tmp - _str; // 返回两个指针之间的间隔距离,即表示找到的子串的起始位置
}
size_t find(char c, size_t pos = 0) const
{
for (size_t i = 0;i < _size;++i)
if (_str[i] == c) return i;
return npos;
}
size_t rfind(char c, size_t pos = 0) const
{
for (size_t i = _size - 1;i != npos;--i)
if (_str[i] == c) return i;
return npos;
}
string substr(size_t pos = 0, size_t len = npos) const
{
assert(pos < _size);
string str;
if (len == npos || pos + len >= _size)
len = _size;
for (size_t i = pos;i < len;++i)
str += _str[i];
return str;
}
// Element access:
char& operator[](size_t n)
{
assert(n >= 0);
assert(n < _size);
return _str[n];
}
const char& operator[](size_t n) const
{
assert(n >= 0);
assert(n < _size);
return _str[n];
}
//Modifiers:
void push_back(char c)
{
assert(_str);
// 先判断是否需要扩容
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 10 : 2 * _capacity;
reserve(newcapacity);
}
_str[_size] = c;
_str[++_size] = '\0'; // 增加字符以后不要忘记将_size增加
}
string& append(const string& str)
{
assert(_str && str._str);
// 先判断是否需要扩容
size_t len = strlen(str._str);
if (_size + len >= _capacity)
{
size_t newcapacity = _size + len + 10; // 每次扩容都多增加是个容积的容错
reserve(newcapacity);
}
memmove(_str + _size, str._str, len);
_str[_size += len] = '\0';
return *this;
}
string& operator+=(char c)
{
push_back(c);
return *this;
}
string& operator+=(const string& str)
{
append(str);
return *this;
}
string& insert(size_t pos, const string& str)
{
assert(pos <= _size);
size_t len = strlen(str._str);
if (_size + len >= _capacity)
reserve(_size + len);
size_t end = _size + len;
while (end - len + 1 > pos)
{
_str[end] = _str[end - len];
--end;
}
memmove(_str + pos, str._str, len);
_size += len;
return *this;
}
string& insert(size_t pos, char c)
{
assert(pos <= _size);
if (_size >= _capacity)
{
size_t newcapacity = _capacity == 0 ? 10 : 2 * _capacity;
reserve(newcapacity);
}
size_t end = _size + 1;
while (end > pos)
{
_str[end] = _str[end - 1];
--end;
}
_str[pos] = c;
++_size;
return *this;
}
string& erase(size_t pos = 0, size_t len = npos)
{
assert(pos < _size);
if (len == npos || pos + len >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
size_t end = pos + len;
while (end <= _size)
{
_str[end - len] = _str[end];
++end;
}
_size -= len;
}
return *this;
}
void swap(string& str)
{
std::swap(_str, str._str);
std::swap(_size, str._size);
std::swap(_capacity, str._capacity);
}
private:
char* _str = nullptr;
size_t _size = 0;
size_t _capacity = 0;
// Member constants:
const static size_t npos = -1;
};
bool operator==(const string& lhs, const string& rhs)
{
return (!(strcmp(lhs._str, rhs._str)) && lhs._size == rhs._size);
}
bool operator!=(const string& lhs, const string& rhs)
{
return !(lhs == rhs);
}
bool operator>(const string& lhs, const string& rhs)
{
return strcmp(lhs._str, rhs._str) > 0;
}
bool operator>=(const string& lhs, const string& rhs)
{
return (lhs > rhs || lhs == rhs);
}
bool operator<(const string& lhs, const string& rhs)
{
return !(lhs >= rhs);
}
bool operator<=(const string& lhs, const string& rhs)
{
return (lhs < rhs || lhs == rhs);
}
// 重载 流插入 & 流提取
std::ostream& operator<<(std::ostream& out, const string& str)
{
for (auto& e : str)
out << e;
return out;
}
std::istream& operator>>(std::istream& in, string& str)
{
str.clear();
char buffer[128] = {};
int count = 0;
for (char ch = in.get();ch != ' ' && ch != '\n';ch = in.get())
{
buffer[count++] = ch;
if (count == 127)
{
str += buffer;
count = 0;
}
}
if (count > 0)
{
buffer[count] = '\0';
str += buffer;
}
return in;
}
std::istream& getline(std::istream& in, string& str)
{
str.clear();
char buffer[128] = {};
int count = 0;
for (char ch = in.get();ch != '\n';ch = in.get())
{
buffer[count++] = ch;
if (count == 127)
{
str += buffer;
count = 0;
}
}
if (count > 0)
{
buffer[count] = '\0';
str += buffer;
}
return in;
}
}