目录
承接CD38.【C++ Dev】string类的模拟实现(2)文章
1.resize
之前在CD36.【C++ Dev】STL库的string的使用 (下)文章提到过resize和reserve的区别,
reserve只是扩容,而resize是扩容+填值初始化(默认填\0) resize会让size和capacity都变(size变的原因是填值初始化)
依照STL标准实现,
可以复用CD37.【C++ Dev】string类的模拟实现(1)文章的reserve函数,代码如下:
参数n可以小于等于或大于或string的大小_size,需要分类讨论:
1.n<=_size
将原本字符串的前n个字符用\0覆盖
2.n>_size
扩容后的字符串的后(n-_size)个字符用\0覆盖
情况1和2都需要修改_size
代码
void resize(size_t n, char c='\0')
{
if (n <= _size)
{
_size = n;//前n个字符不要动,需要保留
_str[_size] = '\0';
}
else
{
reserve(n+1);//+1是放\0
memset(_str + _size, c, n+1 - _size);
_str[n]='\0';//如果c不是\0,需要自行用\0中止
_size = n;
}
}
测试代码
void test15()
{
mystl::string str1("teststring");
mystl::string str2("teststring");
str1.resize(4);
str2.resize(15);
return;
}
下断点到return;
运行结果
void test15()
{
mystl::string str1("teststring");
str1.resize(15,'x');
return;
}
2.operator<<
依照STL标准实现,
放在mystl的命名空间里面
std::ostream& operator<< (std::ostream& os, const string& str)
{
for (char it : str)
os << it;
return os;
}
或者放在mystl的命名空间外面,但string需要改成mystl::string
测试代码
void test16()
{
mystl::string str("teststring");
std::cout << str << std::endl;
return;
}
运行结果
其他说明
operator<<必须引用返回,因为ostream是不能拷贝的,cplusplus网有说明,
https://legacy.cplusplus.com/reference/ostream/ostream/ostream/
3.operator<<
依照STL标准实现,
这样写的错误的:
std::istream& operator>> (std::istream& is, string& str)
{
is >> str._str;
return is;
}
运行以下测试代码:
void test17()
{
mystl::string str;
std::cin >> str;
std::cout << str;
}
void test17()
{
mystl::string str;
str.~string();
std::cin >> str;
std::cout << str;
}
错误的几个原因:
1.虽然_str可以访问指向的内存空间,也有可能出现空间不够的情况,程序可能会越界访问,需要有动态扩容机制
自己实现的构造函数最多可以容纳10个字符
3.没有遵守cin的规则:遇到空格或\0停止读取
解决方法:使用operator+=,会间接调用reserve函数
改成这样仍然有问题:
std::istream& operator>> (std::istream& is, string& str)
{
char ch;
is >> ch;
while (ch != ' ' && ch != '\n')
{
is >> ch;
str += ch;
}
return is;
}
while循环并没有退出,仍然在等待输入:
原因:和cin自身的特点有关系,空格和换行是用于分割的,默认不读空格和换行
解决方法:要将每个字符都读取到,就需要调用get函数
https://legacy.cplusplus.com/reference/istream/istream/get/
版本1
std::istream& operator>> (std::istream& is, string& str)
{
char ch = is.get();
while (ch != ' ' && ch != '\n')
{
str += ch;
ch = is.get();
}
return is;
}
或者将is.get()替换为getchar(),效果等价
运行结果:
继续改进
上面的代码仍然有问题,cin在读取string类字符串之前,会将字符串清空
对比STL库:
void test18()
{
std::string str;
std::cin >> str;
std::cout << str;
std::cout << std::endl << "------------" << std::endl;
std::cin >> str;
std::cout << str;
return;
}
所以应该先调用clear():
void clear()
{
_str[0] = '\0';
_size = 0;
}
版本2
std::istream& operator>> (std::istream& is, string& str)
{
str.clear();
char ch = is.get();
while (ch != ' ' && ch != '\n')
{
str += ch;
ch = is.get();
}
return is;
}
测试代码:
void test18()
{
mystl::string str;
std::cin >> str;
std::cout << str;
std::cout << std::endl << "------------" << std::endl;
std::cin >> str;
std::cout << str;
return;
}
运行结果:
还要清除前导空格和前导换行
char ch= is.get();
while (ch == ' ' || ch == '\n')
ch = is.get();//清除前导空格和前导换行
while (ch != ' ' && ch != '\n')
{
//......
}
运行结果:
优化:版本3
当字符串较长时,+=会造成多次扩容,消耗时间,验证:
每扩容一次就打印一次,修改reserve函数:
运行结果:
解决方法1:提前开辟较大的空间
这样写不太好,如果字符串太短,会造成空间的浪费
解决方法2:使用缓冲数组
定义一个具有固定大小的缓冲数组buffer[256],注意:还要处理剩下的字符,即当buffer还有内容时(i>0)
std::istream& operator>> (std::istream& is, string& str)
{
str.clear();
char buffer[256] = { '\0' };
int i = 0;
char ch= is.get();
while (ch == ' ' || ch == '\n')
ch = is.get();//清除前导空格和前导换行
while (ch != ' ' && ch != '\n')
{
buffer[i++] = ch;
if (i == 255)//最后一个位子给\0
{
buffer[i] = '\0';
str += buffer;
i = 0;
}
ch = is.get();
}
if (i > 0)
{
buffer[i] = '\0';
//如果buffer还有剩余,执行此循环
for (int k = 0; k <= i; k++)
{
str += buffer[k];
}
}
return is;
}
运行结果:
4.字符串比较(relational operators)
分类讨论
字符串长度相等,
字符串长度不相等,不能直接调用strcmp
代码实现
无论字符串长度是否相等,都不能调用strcmp,strcmp比较结束的标志是\0,是否结束看的是string的大小size
看VS的STL库实现
void test20()
{
std::string str1;
std::string str2;
str1.push_back('\0');
str1.push_back('1');
str1.push_back('2');
str2.push_back('\0');
str2.push_back('1');
str2.push_back('3');
std::cout << (str1 < str2) << std::endl;
return;
}
运行结果验证
:很明显越过\0继续向后比较
反汇编验证
下断点到str1 < str2;
转到反汇编后继续下断点:
F11进入call调用的例程:
再下断点到call调用的比较函数compare
F11进入call调用的例程:
发现微软给出的注释:说明是否结束看的是string的大小size
// compare [0, size()) with _Right
可以使用memcmp,用参数num来接收size
自主实现
operator<
若字符串长度相等
bool operator< (const string& s)
{
if (_size == s._size)
return memcmp(_str, s._str, _size-1);//\\末尾的'\0'不用比
else//lhs.size() == rhs.size()
{
//......
}
}
若字符串长度不相等,分两种情况:
1.结果在\0之前就能比较出来
2.结果在\0之后才能比较出来
那就要设两个指针分别遍历:
先比公共部分:
size_t l = 0;
size_t r = 0;
while (l < _size && r < s._size)
{
if (_str[l] < s._str[r])
return 1;
else if (_str[l] > s._str[r])
return 0;
else//_str[l] == s._str[r]则继续比较
{
l++;
r++;
}
}
循环退出的两种情况:l == _size或者r == s._size,之后分别讨论:
if (l < _size&& r == s._size)
{
return 0;
}
if (r < s._size&&l == _size)
{
return 1;
}
return 0;//两字符串相等,可以不用memcmp
因此有:
bool operator< (const string& s) const
{
size_t l = 0;
size_t r = 0;
while (l < _size && r < s._size)
{
if (_str[l] < s._str[r])
return 1;
else if (_str[l] > s._str[r])
return 0;
else//_str[l] == s._str[r]则继续比较
{
l++;
r++;
}
}
if (l < _size&& r == s._size)
{
return 0;
}
if (r < s._size&&l == _size)
{
return 1;
}
return 0;//两字符串相等
}
其实有更简单的写法:
bool operator< (const string& s) const
{
int ret = memcmp(_str, s._str, std::min(_size, s._size));
return ret == 0 ? _size < s._size : ret < 0;
}
memcmp的返回值只有三种:-1,0,1
operator>=
复用operator<
bool operator>=(const string& s) const
{
return !(*this < s);
}
operator==
bool operator==(const string& s) const
{
if (_size != s._size)
return 0;
//_size==s._size
size_t i = 0;
while(i < _size)
{
if (_str[i] != s._str[i])
return 0;
i++;
}
return 1;
}
还有更简单的写法:
bool operator== (const string& s) const
{
//_size==s._size写在memcmp前面,提高运行速度
return _size == s._size && memcmp(_str,s._str,_size)==0
}
operator!=
复用operator==
bool operator != (const string& s) const
{
return !(*this == s);
}
operator>
复用operator>=和operator!=
bool operator>(const string& s) const
{
return (*this >= s) && (*this != s);
}
operator<=
复用operator>
bool operator<=(const string& s) const
{
return !(*this > s);
}
或者复用operator<和operator==
测试题目:
https://www.nowcoder.com/practice/963e455fdf7c4a4a997160abedc1951b
代码:
#include <cstring>
#include <assert.h>
#include <iostream>
#include <algorithm>
using namespace std;
namespace mystl
{
class string
{
public:
typedef char* iterator;
typedef const char* const_iterator;
string(const char* str = "")//""表示一个\0
:_size(strlen(str))
, _capacity(_size + 10)
, _str(new char[_capacity + 1])
{
strcpy(_str, str);
}
const char* c_str() const
{
return _str;
}
char& operator[ ](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
const char& operator[ ](size_t pos) const
{
assert(pos < _size);
return _str[pos];
}
size_t size() const
{
return _size;
}
iterator begin()
{
return _str;
}
const_iterator begin() const
{
return _str;
}
iterator end()
{
return _str + _size;
}
const_iterator end() const
{
return _str + _size;
}
void push_back(char c)
{
if (_size == _capacity)
{
reserve(_capacity * 2);
}
_str[_size++] = c;
_str[_size] = '\0';
}
void reserve(size_t n = 0)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];//+1是为\0准备的
memmove(tmp, _str, _size + 1);//+1是为\0准备的
delete[] _str;
_str = tmp;
_capacity = n;
//std::cout << "reserve扩容为:" << _capacity << std::endl;
}
}
string& append(const char* str)
{
size_t len = strlen(str);
if (len + _size > _capacity)
reserve(len + _size);
memmove(_str + _size, str, len + 1);
_size += len;
return *this;
}
string& operator+=(char c)
{
push_back(c);
return *this;
}
string& operator+=(const char* s)
{
append(s);
return *this;
}
string& insert(size_t pos, size_t n, char c)
{
assert(pos <= _size);
if (pos + n > _capacity)//注意不是和_size比较
reserve(pos + n);//至少扩容到pos + n
size_t i = _size;//_str[i]为\0
while (i >= pos && i != npos)
{
_str[i + n] = _str[i];
i--;
}
memset(_str + pos, c, n);
_size += n;
return *this;
}
string& insert(size_t pos, const char* s)
{
size_t n = strlen(s);
assert(pos <= _size);
if (pos + n > _size)
{
reserve(pos + n);
}
size_t i = _size;//_str[i]为\0
while (i >= pos && i != npos)
{
_str[i + n] = _str[i];
i--;
}
memmove(_str + pos, s, n);
_size += n;
return *this;
}
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
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 i = pos + len;
while (i < _size)
_str[pos++] = _str[i++];
_str[pos + len] = '\0';
_size -= len;
}
return *this;
}
size_t find(const string& str, size_t pos = 0) const
{
assert(pos < _size);
const char* ret_ptr = strstr(_str, str.c_str());
if (ret_ptr == nullptr)
return npos;
return ret_ptr - _str;
}
size_t find(char c, size_t pos = 0) const
{
assert(pos < _size);
for (size_t i = pos; i < _size; i++)
if (_str[i] == c)
return i;
return npos;
}
string(const string& str)
{
_capacity = str._capacity;
_size = str._size;
_str = new char[_capacity + 1];
memmove(_str, str._str, _capacity + 1);
}
string substr(size_t pos = 0, size_t len = npos) const
{
assert(pos < _size);
string ret;
ret._size = len;
if (len == npos || pos + len >= _size)//从pos处截取到末尾
ret._size = _size - pos;
ret.reserve(ret._size);
memmove(ret._str, _str + pos, ret._size);
if (ret._str[ret._size] != '\0')
ret._str[ret._size] = '\0';
return ret;
}
void resize(size_t n, char c = '\0')
{
if (n <= _size)
{
_size = n;
_str[_size] = '\0';
}
else
{
reserve(n + 1);
memset(_str + _size, c, n + 1 - _size);
_str[n] = '\0';
_size = n;
}
}
void clear()
{
_str[0] = '\0';
_size = 0;
}
bool operator< (const string& s) const
{
size_t l = 0;
size_t r = 0;
while (l < _size && r < s._size)
{
if (_str[l] < s._str[r])
return 1;
else if (_str[l] > s._str[r])
return 0;
else//_str[l] == s._str[r]则继续比较
{
l++;
r++;
}
}
if (l < _size && r == s._size)
{
return 0;
}
if (r < s._size && l == _size)
{
return 1;
}
return 0;//两字符串相等
}
bool operator>=(const string& s) const
{
return !(*this < s);
}
bool operator==(const string& s) const
{
if (_size != s._size)
return 0;
//_size==s._size
size_t i = 0;
while(i < _size)
{
if (_str[i] != s._str[i])
return 0;
i++;
}
return 1;
}
bool operator != (const string& s) const
{
return !(*this == s);
}
bool operator>(const string& s) const
{
return (*this >= s) && (*this != s);
}
bool operator<=(const string& s) const
{
return !(*this > s);
}
friend std::istream& operator>> (std::istream& is, string& str);
private:
size_t _size;
size_t _capacity;
char* _str;
const static size_t npos;
};
const size_t string::npos = -1;
std::ostream& operator<< (std::ostream& os, const string& str)
{
for (char it : str)
os << it;
return os;
}
std::istream& operator>> (std::istream& is, string& str)
{
str.clear();
char buffer[256] = { '\0' };
int i = 0;
char ch = is.get();
while (ch == ' ' || ch == '\n')
ch = is.get();//清除前导空格和前导换行
while (ch != ' ' && ch != '\n')
{
buffer[i++] = ch;
if (i == 255)//最后一个位子给\0
{
buffer[i] = '\0';
str += buffer;
i = 0;
}
ch = is.get();
}
if (i > 0)
{
buffer[i] = '\0';
//如果buffer还有剩余,执行此循环
for (int k = 0; k <= i; k++)
{
str += buffer[k];
}
}
return is;
}
}
int mystrcmp(const char* src, const char* dst);
int main() {
char s1[100] = { 0 };
char s2[100] = { 0 };
cin.getline(s1, sizeof(s1));
cin.getline(s2, sizeof(s2));
int ret = mystrcmp(s1, s2);
cout << ret << endl;
return 0;
}
int mystrcmp(const char* src,const char* dst)
{
mystl::string str1=src;
mystl::string str2=dst;
if (str1 > str2)
return 1;
else if (str1 == str2)
return 0;
else //str1 < str2
return -1;
}
提交结果: