目录
前言
一、模拟实现string
1. 成员变量
2. 构造函数
2.1 构造函数
2.2 重载默认构造
2.3 合并
3. 析构函数
4. 拷贝构造函数
5. c_str
6. size
7. operator[ ]
7.1 普通版
7.2 const版本
8. 迭代器—iterator
8.1 普通版iterator
8.2 const版本iterator
9. 尾插
10. operator+=
11. insert
11.1 插入n个字符
11.2 插入字符串
12. erase
12.1 从pos位置开始删除
13. find
13.1 查找字符
13.2 查找字符串
14. substr
15. reseve && resize
16. operator<< 流插入
17. operator>> 流提取
18. string比较
19. operator= 赋值运算符重载
二、整体代码
总结
学习了string的用法,为了提升自身能力,我们来模拟实现string的部分功能
初始化列表初始化顺序是声明顺序!
我们将自己实现的string放在自己的命名空间内,防止与std中的string冲突
namespace my_string
{
class string
{
};
};
private:
char* _str;
size_t _size;
size_t _capacity;
构造函数功能:为字符串开空间,并根据传入的参数进行初始化操作。
此处会出现一些问题,当我们使用初始化列表时,每个人的初始化顺序可能不同,并且成员变量的声明顺序也可能不同,编写完代码后,发现出错,代码不能跑,这就是初始化列表的知识点——初始化列表的初始化顺序由成员变量的声明顺序决定。
并且为了少次使用strlen这一O(N)级函数,我们可以只将一个变量在初始化列表初始化,其他成员在函数内赋值(以达到少次使用strlen函数),这是因为这些成员变量是内置类型(内置类型在列表或构造函数体内初始化区别不大),如果是自定义类型则不能在内部赋值,因为即使不写,它们仍然会被自动调用各自的构造函数。
这里我们将全部的成员变量都放在函数体内赋值,以便代码清晰。
namespace my_string
{
class string
{
public:
string(const char* str)
{
_size = strlen(str);
_capacity = _size;
//加1,存放\0
_str = new char[_capacity + 1];
strcpy(_str, str);
}
private:
size_t _size;
size_t _capacity;
char* _str;
};
};
那么,当我们不传参构造,只实例化时,编译器找不到默认构造函数,所以我们要重载一个默认构造函数。
根据监视,可以看到C++的string在无参构造时,它是有一个‘\0'的,所以我们在初始化_str时不能置空,要至少开一个空间,来存储 '\0'
string(const char* str)
:_size(0)
,_capacity(0)
,_str(new char[1])
{
_str[0] = '\0';
}
我们可以使用缺省值,将两个构造函数合并
这里的问题在于,如何设置缺省值?是 '\0' ,还是 nullptr, 还是 "\0" ?
- '\0' 不可以,问题在于左边类型为char*,而'\0'是字符,两者类型不同
- nullptr 也不可以,问题在于后面的strlen直接失效,并且没有 '\0'
- "\0" 不太合适,因为 “” 内默认有\0,所以缺省值应为“”
string(const char* str = "")
{
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
注意:该构造函数代码之后会在operator<<处再次修改
- 释放new出的空间,并将_size与_capacity置空即可
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
//拷贝构造
string(const string& s)
{
_str = new char[s._capacity + 1];
strcpy(_str, s._str);
_size = s._size;
_capacity = s._capacity;
}
注意:该构造函数代码之后会在operator<<处再次修改
- 模拟string中的c_str函数功能,返回c形式的字符串,即返回 _str
- 在函数后面加上const,可以使被const修饰的对象也可调用该函数
const char* c_str() const
{
return _str;
}
- 返回_size即可
size_t size() const
{
return _size;
}
- 为了可读可写,我们使用引用返回,因为返回的对象是数组内的元素,生命周期不在operator[ ]函数内,所以可以使用引用返回
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
- const版本只能读,与普通版本形成函数重载,调用函数时,编译器会调用最匹配的函数
//const版本 只读
const char& operator[](size_t pos) const
{
assert(pos < _size);
return _str[pos];
}
- 对于string类,迭代器可以使用指针实现,即迭代器就是指针(针对string类)
//迭代器,在string这里就是指针
typedef char* iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
测试:
#include
#include"string模拟.h"
using namespace my_string;
using std::cout;
using std::endl;
void test()
{
my_string::string s1("hello world");
cout << s1.c_str() << endl;
string::iterator it = s1.begin();
while (it != s1.end())
{
cout << *it << " ";
++it;
}
cout << endl;
for (auto ch : s1)
{
cout << ch << "";
}
cout << endl;
}
hello world
h e l l o w o r l d
hello world
可以看到,我们在写完迭代器之后,范围for就可以直接使用了,因为它就是利用迭代器实现的,底层与使用迭代器遍历几乎一样。
如果我们把end(),改为End(),那么范围for就不可用了。所以,范围for是傻瓜式替换,找到类中的begin(),end(),字母错一个它都实现不了。
- 当对象被const修饰,那么迭代器就要加上const,保证值不被修改
typedef const char* const_iterator;
const_iterator begin() const
{
return _str;
}
const_iterator end() const
{
return _str + _size;
}
string::const_iterator cit = s1.begin();
while (cit != s1.end())
{
cout << *cit << endl;
++cit;
}
cout << endl;
- 判断是否要扩容,对于push_back() 函数可以进行2倍扩容;对于append() 函数,要进行大小判断,如果_size + strlen(str) > _ capacity 那么至少扩容到_size + len 的容量大小。
- 对于扩容操作,我们实现reserve() 函数,进行扩容处理
void reserve(size_t n)
{
if (n > _capacity)
{
//多开一个空间留给 '\0'
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
void push_back(char ch)
{
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
_str[_size] = ch;
++_size;
_str[_size] = '\0';
}
void append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len);
}
strcpy(_str + _size, str);
_size += len;
}
- 实现尾插之后,operator+=的实现就非常简单了,+=是有返回值的,返回的是string类型
- 可以返回*this,因为*this是对象,对象的生命周期不在operator+=中,所以可以返回*this
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
- 先判断下标是否合法
- 再判断是否要扩容
- 移位,移位时有“坑”,因为是size_t 类型,大于等于0,-1是最大正数,如果end改为int类型也不行,因为会发生隐式类型转换。这里有多种解决方案:可以强制类型转换、可以将end从end+n开始,进行end - n 赋给 end ,这样就不会到下标为0的地方、用npos判断(npos使用静态成员函数,赋值为-1,表示最大值)
- 插入数据
- 更新_size
void insert(size_t pos, size_t n, char ch)
{
//size_t大于0,不用判断
assert(pos <= _size);
if (_size + n > _capacity)
{
//至少扩容到 _size + n
reserve(_size + n);
}
size_t end = _size;
//size_t因为不小于0,当pos为0时,会陷入死循环
//这里有多种方案,可以强制类型转换、可以将end从end+n开始、用npos判断
while (end >= pos && end != npos)
{
_str[end + n] = _str[end];
--end;
}
for (size_t i = 0; i < n; i++)
{
_str[pos + i] = ch;
}
_size += n;
}
- 同理,将n替换为len即可
void 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;
while (end >= pos && end != npos)
{
_str[end + len] = _str[end];
--end;
}
for (size_t i = 0; i < len; i++)
{
_str[pos + i] = str[i];
}
_size += len;
}
- 先判断下标合法
- 再判断是不是要删到尾部,如果是,直接把pos处改为 '\0' ,并修改_size值即可
- 如果不是,就是在内部删除,和insert操作类似,移动
void erase(size_t pos, size_t len = npos)
{
assert(pos < _size);
if (len == npos || pos + len >= _size)
{
_str[pos] = '\0';
_size = pos;
_str[_size] = '\0';
}
else
{
size_t begin = pos + len;
//等于size,把\0也移动过去
while (begin <= _size)
{
_str[pos++] = _str[begin++];
}
_size -= len;
}
}
size_t find(char ch, size_t pos = 0)
{
assert(pos < _size);
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == ch)
return i;
}
//找不到,返回npos
return npos;
}
size_t find(const char* str, size_t pos = 0)
{
assert(pos < _size);
const char* ptr = strstr(_str, str);
if (ptr)
{
return ptr - _str;
}
else
{
return npos;
}
}
- 注意for循环中 i < pos + n,别误写为 i < n
string substr(size_t pos, size_t len = npos)
{
assert(pos < _size);
size_t n = len;
if (len == npos || pos + len > _size)
{
n = _size - pos;
}
string tmp;
tmp.reserve(n);
//注意i < pos + n
for (size_t i = pos; i < pos + n; i++)
{
tmp += _str[i];
}
return tmp;
}
void reserve(size_t n)
{
if (n > _capacity)
{
//多开一个放'\0'
char* tmp = new char[n + 1];
//strcpy(tmp, _str);
memcpy(tmp, _str, _size + 1);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
void resize(size_t n, char ch = '\0')
{
if (n < _size)
{
_size = n;
_str[_size] = '\0';
}
else
{
//利用reserve进行扩容判断
reserve(n);
for (size_t i = _size; i < n; i++)
{
_str[i] = ch;
}
_size = n;
//由于ch不一定是\0,而且i < n没到最后的\0位置
_str[_size] = '\0';
}
}
流插入,有多少字符就输出多少字符,中间有'\0'也不终止
注意将该函数放在命名空间域中,还是全局函数
//流插入,有多少字符就输出多少字符,中间有'\0'也不终止
std::ostream& operator<<(std::ostream& out, const string& s)
{
//要访问私有变量才用友元
for (auto i : s)
{
out << s[i];
}
return out;
}
直接使用 c_str 和 << 在一般情况是相同的,但是c_str是c字符串,遇到\0自动终止,而string则是依靠size来判断,size有多大,operator<<就打印多长
例如 :
void test() { //与c的字符串不同,c的字符串遇到\0终止,而string是直到size终止 string ss("hello world"); ss += '\0'; ss += "LLLLLLLLLL"; cout << ss.c_str() << endl; cout << ss << endl; } int main() { test(); return 0; }
hello world hello wordl LLLLLLLLLL
可以看到第二个打印有空格,空格表示了 '\0'
所以,有了\0,之前用的str系列的函数就需要修改了,因为str类型的函数遇到\0自动结束,而string是遇到size才停止,将之前的str函数改为memcpy函数
- 对于构造函数,可以不修改,因为本身构造时,用的就是c字符串,strlen以及strcpy都不需要修改,这里修改strcpy是为了与下面一致
//构造 string(const char* str = "") { _size = strlen(str); _capacity = _size; _str = new char[_capacity + 1]; memcpy(_str, str, _size + 1); } //拷贝构造 string(const string& s) { _str = new char[s._capacity + 1]; memcpy(_str, s._str, s._size + 1); _size = s._size; _capacity = s._capacity; }
- 对于reserve也需要更改
- 对于append可以不更改,因为追加的是c字符串,为了一致性,改为memcpy,拷贝到时候都默认加一个1,因为strcpy默认拷贝\0
void append(const char* str) { size_t len = strlen(str); if (_size + len > _capacity) { reserve(_size + len); } //strcpy(_str + _size, str); memcpy(_str + _size, str, len + 1);
//流提取(从终端提取数据),这里的s不能使用const,因为要往s里存放数据
std::istream& operator>>(std::istream& in, string& s)
{
//不能写成 in >> s.str,因为s可能没空间来存放数据
char ch;
in >> ch;
while (ch != ' ' && ch != '\n')
{
s += ch;
in >> ch;
}
return in;
}
上面的代码有错误,当我们输入连续的字符时,不能读到空格和换行,就会陷入死循环,这是in本身的设定,以空格和换行符来区分多个数据,所以我们要使用 get 来提取终端数据 ——in.get(),可以读空格和换行
当我们不想以空格为分隔符时,可以将 ch != ' ' 判断条件去掉
//流提取(从终端提取数据),这里的s不能使用const,因为要往s里存放数据
std::istream& operator>>(std::istream& in, string& s)
{
//不能写成 in >> s.str,因为s可能没空间来存放数据
char ch = in.get();
while (ch != ' ' && ch != '\n')
{
s += ch;
ch = in.get();
}
return in;
}
对于cin操作,如果多次cin,应该会覆盖原先数据 ,即我们需要实现清理工作clear()
void clear()
{
_str[0] = '\0';
_size = 0;
}
//流提取(从终端提取数据),这里的s不能使用const,因为要往s里存放数据
std::istream& operator>>(std::istream& in, string& s)
{
s.clear();
//不能写成 in >> s.str,因为s可能没空间来存放数据
char ch = in.get();
while (ch != ' ' && ch != '\n')
{
s += ch;
ch = in.get();
}
return in;
}
优化:循环里的+=效率不高,因为如果字符串过长可能经历多次扩容。我们当然可以提前reserve开一定空间,但是当字符串过短,那么如果空间开的过大就浪费了。
在这里,我们开一个128的char数组,暂存从终端提取的数据,积累一些数据,当数据超过数组大小后,尾插 \0 后将该数组+=上去,并将下标置为0,再次提取数据。
当数据不够128时,跳出循环,所以要再循环外判断数组内是否还存有数据,再进行+=
std::istream& operator>>(std::istream& in, string& s) { s.clear(); //不能写成 in >> s.str,因为s可能没空间来存放数据 char ch = in.get(); char buff[128]; int i = 0; while (ch != ' ' && ch != '\n') { buff[i++] = ch; if (i == 127) { buff[i] = '\0'; s += buff; i = 0; } ch = in.get(); } if (i != 0) { buff[i] = '\0'; s += buff; } return in; }
但是对于前有空格和换行,后面输入的数据就进不去,所以再进行优化,加一个while循环,将前空格和换行去除。
//流提取(从终端提取数据),这里的s不能使用const,因为要往s里存放数据 std::istream& operator>>(std::istream& in, string& s) { s.clear(); //不能写成 in >> s.str,因为s可能没空间来存放数据 char ch = in.get(); //处理前缓冲区-流-前面的空格或者换行 while (ch == ' ' || ch == '\n') { ch = in.get(); } char buff[128]; int i = 0; while (ch != ' ' && ch != '\n') { buff[i++] = ch; if (i == 127) { buff[i] = '\0'; s += buff; i = 0; } ch = in.get(); } if (i != 0) { buff[i] = '\0'; s += buff; } return in; }
- string按照ASCII进行比较大小
- 不能用strcmp,因为当遇到 例如:hello\0xxxxx 和 hello\0yyyyy 时,比较遇到 \0 就结束了,所以我们可以使用memcmp或手写
- 加const防止改变this值,以及可以使const对象调用该函数
//方法一:
bool operator<(const string& s) const
{
int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);
return ret == 0 ? _size < s._size : ret < 0;
}
方法二:
//bool operator<(const string& s)
//{
// //return strcmp(_str, s._str) < 0;由于\0,所以不能用strcmp
// size_t i1 = 0;
// size_t i2 = 0;
// while (i1 < _size && i2 < s._size)
// {
// if (_str[i1] < s._str[i2])
// return true;
// else if (_str[i1] > s._str[i2])
// return false;
// else
// {
// ++i1;
// ++i2;
// }
// }
// //在循环之外,只有一种情况是true
// //return i1 == _size && i2 != s._size;
// //或者判断size大小
// return _size < s._size;
//}
其他比较复用即可
bool operator==(const string& s) const
{
//先比较size过滤各种情况
return _size == s._size
&& memcmp(_str, s._str, _size < s._size) == 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& operator=(const string& s)
{
if (this != &s)
{
//先开空间,赋值,再释放,赋值
char* tmp = new char[s._capacity];
memcpy(tmp, s._str, s._size + 1);
delete[] _str;
_str = tmp;
_size = s._size;
_capacity = s._capacity;
}
return *this;
}
方法二:交换
先根据s创建一个临时对象,再将临时对象和this的各个数据进行交换,这时this就指向了拷贝对象,临时对象tmp就指向了原空间,tmp出了作用域自动销毁,完美解决
//方法二:swap交换
void swap(string& s)
{
std::swap(s._str, _str);
std::swap(s._size, _size);
std::swap(s._capacity, _capacity);
}
string& operator=(const string& s)
{
if (this != &s)
{
//不能直接交换两个对象,会发生栈溢出、无穷递归
//std::swap(tmp, *this); 一直调用赋值运算符重载
string tmp(s);
//this->swap(tmp);
swap(tmp);
}
//tmp是局部对象,出函数自动销毁
return *this;
}
方法三:再次优化,将形参改为string tmp,直接拷贝构造tmp,出函数作用域直接析构
void swap(string& s)
{
std::swap(s._str, _str);
std::swap(s._size, _size);
std::swap(s._capacity, _capacity);
}
//方法三:改变行参,直接拷贝构造tmp
string& operator=(string tmp)
{
swap(tmp);
return *this;
}
my_string.h
#pragma once
#include
#include
#include
namespace my_string
{
class string
{
public:
//迭代器,在string这里就是指针
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
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];
memcpy(_str, str, _size + 1);
}
//拷贝构造
string(const string& s)
{
_str = new char[s._capacity + 1];
memcpy(_str, s._str, s._size + 1);
_size = s._size;
_capacity = s._capacity;
}
赋值运算符重载——深拷贝
//string& operator=(const string& s)
//{
// if (this != &s)
// {
// char* tmp = new char[s._capacity];
// memcpy(tmp, s._str, s._size + 1);
// delete[] _str;
// _str = tmp;
// _size = s._size;
// _capacity = s._capacity;
// }
// return *this;
//}
//方法二:swap交换
void swap(string& s)
{
std::swap(s._str, _str);
std::swap(s._size, _size);
std::swap(s._capacity, _capacity);
}
//string& operator=(const string& s)
//{
// if (this != &s)
// {
// //不能直接交换两个对象,会发生栈溢出、无穷递归
// //std::swap(tmp, *this); 一直调用赋值运算符重载
// string tmp(s);
// //this->swap(tmp);新手才写this o.0
// swap(tmp);
// }
// //tmp是局部对象,出函数自动销毁
// return *this;
//}
//方法三:改变行参,直接拷贝构造tmp
string& operator=(string tmp)
{
swap(tmp);
return *this;
}
//析构
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 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版本 只读
const char& operator[](size_t pos) const
{
assert(pos < _size);
return _str[pos];
}
void reserve(size_t n)
{
if (n > _capacity)
{
//多开一个放'\0'
char* tmp = new char[n + 1];
//strcpy(tmp, _str);
memcpy(tmp, _str, _size + 1);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
void resize(size_t n, char ch = '\0')
{
if (n < _size)
{
_size = n;
_str[_size] = '\0';
}
else
{
//利用reserve进行扩容判断
reserve(n);
for (size_t i = _size; i < n; i++)
{
_str[i] = ch;
}
_size = n;
//由于ch不一定是\0,而且i < n没到最后的\0位置
_str[_size] = '\0';
}
}
void push_back(char ch)
{
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
_str[_size] = ch;
++_size;
_str[_size] = '\0';
}
void append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len);
}
//strcpy(_str + _size, str);
memcpy(_str + _size, str, len);
_size += len;
}
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
void insert(size_t pos, size_t n, char ch)
{
//size_t大于0,不用判断
assert(pos <= _size);
if (_size + n > _capacity)
{
//至少扩容到 _size + n
reserve(_size + n);
}
size_t end = _size;
//size_t因为不小于0,当pos为0时,会陷入死循环
//这里有多种方案,可以强制类型转换、可以将end从end+n开始、用npos判断
while (end >= pos && end != npos)
{
_str[end + n] = _str[end];
--end;
}
for (size_t i = 0; i < n; i++)
{
_str[pos + i] = ch;
}
_size += n;
}
void 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;
while (end >= pos && end != npos)
{
_str[end + len] = _str[end];
--end;
}
for (size_t i = 0; i < len; i++)
{
_str[pos + i] = str[i];
}
_size += len;
}
void erase(size_t pos, size_t len = npos)
{
assert(pos < _size);
if (len == npos || pos + len >= _size)
{
_str[pos] = '\0';
_size = pos;
_str[_size] = '\0';
}
else
{
size_t begin = pos + len;
//等于size,把\0也移动过去
while (begin <= _size)
{
_str[pos++] = _str[begin++];
}
_size -= len;
}
}
string substr(size_t pos, size_t len = npos)
{
assert(pos < _size);
size_t n = len;
if (len == npos || pos + len > _size)
{
n = _size - pos;
}
string tmp;
tmp.reserve(n);
//注意i < pos + n
for (size_t i = pos; i < pos + n; i++)
{
tmp += _str[i];
}
return tmp;
}
size_t find(char ch, size_t pos = 0)
{
assert(pos < _size);
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == ch)
return i;
}
//找不到,返回npos
return npos;
}
size_t find(const char* str, size_t pos = 0)
{
assert(pos < _size);
const char* ptr = strstr(_str, str);
if (ptr)
{
return ptr - _str;
}
else
{
return npos;
}
}
void clear()
{
_str[0] = '\0';
_size = 0;
}
//方法一:
bool operator<(const string& s) const
{
int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);
return ret == 0 ? _size < s._size : ret < 0;
}
方法二:
//bool operator<(const string& s)
//{
// //return strcmp(_str, s._str) < 0;由于\0,所以不能用strcmp
// size_t i1 = 0;
// size_t i2 = 0;
// while (i1 < _size && i2 < s._size)
// {
// if (_str[i1] < s._str[i2])
// return true;
// else if (_str[i1] > s._str[i2])
// return false;
// else
// {
// ++i1;
// ++i2;
// }
// }
// //在循环之外,只有一种情况是true
// //return i1 == _size && i2 != s._size;
// //或者判断size大小
// return _size < s._size;
//}
bool operator==(const string& s) const
{
//先比较size过滤各种情况
return _size == s._size
&& memcmp(_str, s._str, _size < s._size) == 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);
}
private:
size_t _size;
size_t _capacity;
char* _str;
static size_t npos;
};
size_t string::npos = -1;
//流插入(向终端插入数据),有多少字符就输出多少字符,中间有'\0'也不终止
std::ostream& operator<<(std::ostream& out, const string& s)
{
//要访问私有变量才用友元
for (auto ch : s)
{
out << ch;
}
return out;
}
//流提取(从终端提取数据),这里的s不能使用const,因为要往s里存放数据
std::istream& operator>>(std::istream& in, string& s)
{
s.clear();
//不能写成 in >> s.str,因为s可能没空间来存放数据
char ch = in.get();
//处理前缓冲区前面的空格或者换行
while (ch == ' ' || ch == '\n')
{
ch = in.get();
}
char buff[128];
int i = 0;
while (ch != ' ' && ch != '\n')
{
buff[i++] = ch;
if (i == 127)
{
buff[i] = '\0';
s += buff;
i = 0;
}
ch = in.get();
}
if (i != 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}
};
我们模拟实现的string,具有部分功能,还有一部分没有实现,有兴趣的同学可以参照std的string再进行模拟实现,下节我们将讲解vector并进行实现。
最后,如果小帅的本文哪里有错误,还请大家指出,请在评论区留言(ps:抱大佬的腿),新手创作,实属不易,如果满意,还请给个免费的赞,三连也不是不可以(流口水幻想)嘿!那我们下期再见喽,拜拜!