安庆网站优化网店推广平台
1.string类
在使用string类时,必须包含<string>头文件以及using namespace std;
接下来我们看看string
类是如何被声明的:
typedef basic_string<char> string;
可以看到:string类是被类模板basic_string用数据类型char实例化后得到的一个具体的类的别名。
同时规定:
1.string是表示字符串的字符串类;
2.该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作;
3.string在底层实际是:basic_string模板类的别名,typedef basic_string string;
4.不能操作多字节或者变长字符的序列。
1.1 string类的成员函数及其基本用法
1.1.1 默认成员函数
默认成员函数包括,constructor构造函数、destructor ~string析构函数、赋值运算符重载operator=。
1.1.2 constructor--构造函数
string类的构造函数string()被重载了许多形式,我们只需要重点掌握以下三种即可:
string();//构造一个空字符串
string(const string& str);//拷贝构造
string(const char* s);//用一个常量字符串来构造一个string类对象
具体用法:
#include<iostream>
#include<string>
using namespace std;//string类的成员函数及其基本用法
//1、构造函数
void constructor_test()
{string s1;//构造一个空字符串string s2("hello world");//使用一个常量字符串来构造出一个string类对象string s3(s2);//拷贝构造//在string类中已经重载了流插入<< 和流提取>> 运算符,故可以直接使用<<来输出string字符串对象cout << "字符串s1:" << s1 << endl;cout << "字符串s2:" << s2 << endl;cout << "字符串s3:" << s3 << endl;
}int main()
{constructor_test();return 0;
}
output:
字符串s1:
字符串s2:hello world
字符串s3:hello world
1.1.3 operator= ----赋值运算符重载
string& operator=(const string& str);//类类型之间的赋值
string& operator=(const char* s);//利用隐式类型转换,先将字符串s实例化为一个string类型对象,再进行赋值
string& operator=(char c);//和第二种方法类似
具体用法:
//operator= 赋值运算符重载
void test()
{string s1, s2, s3;s1 = "hello world";s2 = 'c';s3 = s1;cout << "s1字符串为:" << s1 << endl;cout << "s2字符串为:" << s2 << endl;cout << "s3字符串为:" << s3 << endl;
}int main()
{test();return 0;
}
output:
s1字符串为:hello world
s2字符串为:c
s3字符串为:hello world
1.1.4 string类对象的容量操作
1.1.4.1 size/length----返回字符串有效字符长度
size_t size() const;
size_t length() const;
这两个函数的效果是一样的,都是返回字符串有效字符的长度,但是更建议使用函数size()。字符串的有效长度是不包括结束字符'\0'的,其结构就和C语言的strlen()函数一样。
size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本上都是用size()。
//3、string类对象的容量操作
//(1)size--返回字符串有效字符的长度
void test_2()
{string s1;string s2("hello world");cout << "size of s1:" << s1.size() << endl;cout << "size of s2:" << s2.size() << endl;
}int main()
{test_2();return 0;
}
output:
size of s1:0
size of s2:11
1.1.4.2 capacity----返回空间总大小
size_t capacity() const;
一般来说,这个最大容量指的是可以存放有效字符的最大容量,也不包括结束符'\0'。需要注意的是,由于不同平台所用的库不同,因此用同一个字符串构造string对象时,分配给其用来存储字符的初始空间也不一定相同(即capacity不一定相同),例如:
//(2)capacity--获取最大容量
void test_3()
{string s1;string s2("hello world hello everyone");cout << "capacity of s1:" << s1.capacity() << endl;cout << "capacity of s2:" << s2.capacity() << endl;
}int main()
{test_3();return 0;
}
在VS下,output:
capacity of s1:15
capacity of s2:31
在Linux下,output:
capacity of s1 is: 0
capacity of s2 is: 16
1.1.4.3 clear/resize
void clear();//清空有效字符
void resize (size_t n);
void resize (size_t n, char c);//将有效字符的个数改成n个,多出的空间用字符c填充
clear()只是将string中有效字符清空,不改变底层空间的大小。
resize(size_t n)与resize(size_t n,char c)都是字符串中有效字符个数改变到n个,不同的是当字符个数增多时,resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
//(4)clear--清空有效字符,
// resize--将有效字符的个数该成n个,多出的空间用字符c填充
void test_4()
{string s("hello world!");cout << "size of s:" << s.size() << endl;cout <<"capacity of s:" << s.capacity() << endl;cout << s << endl;cout << "============" << endl;//将s中的字符串清空,注意清空时只是将size清零,不改变底层空间的大小s.clear();cout << "size of s:" << s.size() << endl;cout << "capacity of s:" << s.capacity() << endl;cout << "============" << endl;//将s中的有效字符个数增加到10个,多出的位置用字符'a'进行填充//"aaaaaaaaaa"s.resize(10, 'a');cout << "size of s:" << s.size() << endl;cout << "capacity of s:" << s.capacity() << endl;cout << s << endl;cout << "============" << endl;//将s中有效字符个数增加到15个,多出位置用缺省值'\0'进行填充//"aaaaaaaaaa\0\0\0\0\0"//此时s中有效字符个数以及增加到了15个s.resize(15);cout << "size of s:" << s.size() << endl;cout << "capacity of s:" << s.capacity() << endl;cout << s << endl;cout << "============" << endl;//将s中的有效字符个数缩小到5个s.resize(5);cout << "size of s:" << s.size() << endl;cout << "capacity of s:" << s.capacity() << endl;cout << s << endl;
}int main()
{test_4();return 0;
}
output:
size of s:12
capacity of s:15
hello world!
============
size of s:0
capacity of s:15
============
size of s:10
capacity of s:15
aaaaaaaaaa
============
size of s:15
capacity of s:15
aaaaaaaaaa
============
size of s:5
capacity of s:15
aaaaa
1.1.4.4 reserve 为字符串预留空间
void reserve (size_t n = 0);
reserve成员函数会将string的最大容量capacity扩展为n,reserve()只会对capacity做出改变,而不会影响原来的数据(既不会删除也不会创建)。当reserve的参数小于string的底层空间总大小时,reserve不会改变容量大小。
需要注意,由于不同平台依赖的库不同,所以reserve最终的效果也会不同,但是无论如何,reserve()绝不会影响到存储的数据。
//4、reserve--为字符串预留空间
void test_5()
{string s1("hello world!");string s2 = s1;cout << "the capacity of s1:" << s1.capacity() << endl;s1.reserve(100);cout << "after reserve(100),the capacity of s1:" << s1.capacity() << endl;s1.reserve(50);cout << "after reserve(50),the capacity of s1:" << s1.capacity() << endl;cout << "the capacity of s2:" << s2.capacity() << endl;s2.reserve(1);cout << "after reserve(1),the capacity of s2:" << s2.capacity() << endl;
}int main()
{test_5();return 0;
}
VS:output
the capacity of s1:15
after reserve(100),the capacity of s1:111
after reserve(50),the capacity of s1:111
the capacity of s2:15
after reserve(1),the capacity of s2:15
Linux:output
the capacity of s1:11
after reserve(100), the capacity of s1:100
after reserve(50), the capacity of s1:50the capacity of s2:11
after reserve(1), the capacity of s2:11
可以总结出两者之间的不同:VS在分配空间时,总会比给定的值多几个空间;而Linux则是给多少开多少空间。在VS下reserve()函数不能缩小空间,只能扩大空间;而Linux操作系统下,reserve()函数可以缩小空间,同时他们有一个共同点:无论给定值再怎么小,reserve()函数都不会影响到原来的数据。
1.1.5 string类对象的访问及遍历操作
1.1.5.1 operator[]/at()--获取指定位置的字符
char& operator[] (size_t pos);
const char& operator[] (size_t pos) const;char& at (size_t pos);
const char& at (size_t pos) const;
相同点:和普通字符数组一样,string类类型的对象也可以通过类似[下标]的方式获得指定位置的字符,这个函数被重载成了两份,分别给非const对象和const对象使用。
不同点:如果传入的pos大于size(),那么对于operator[],则会直接报错,而对于at(),则会抛出异常。有了这两个成员函数,我们就可以对string对象存储的数据进行遍历访问了。
//5、operator[] / at()
void test_6()
{string s("hello world");cout << s << endl;for (int i = 0; i < s.size(); ++i)cout << s[i] << ' ';cout << endl;for (int i = 0; i < s.size(); ++i)cout << s.at(i) << ' ';cout << endl;
}int main()
{test_6();return 0;
}
output:
hello world
h e l l o w o r l d
h e l l o w o r l d
1.1.5.2 iterator——迭代器
迭代器是一个用来访问容器数据的对象,其提供了统一的方式来遍历容器中的数据,对于string类,我们可以将迭代器看成一个指针,其指向string对象存储的某个字符。我们可以通过迭代器来访问或者修改容器中的数据。尽管前面的[]运算符可以访问和修改string存储的数据,但是在STL中,对于访问和遍历数据迭代器才是最常用的。
以下是几种获取string类型迭代器的常见方式:
1.1.5.2.1 begin()/end()
iterator begin();
const_iterator begin() const;
###############################
iterator end();
const_iterator end() const;
begin()即返回一个指向字符序列第一个字符的迭代器;end()返回一个指向字符序列最后一个字符(即'\0')的迭代器。begin() const 和 end() const 是针对const对象做出的函数重载。
注意:
由于string类实际上就是存储字符序列的类,因此针对它的迭代器iterator,我们可以将其看成为一个指向char类型的指针char*,而const_iterator对应的是const char*。
但是为什么const_iterator不写成const iterator呢?对于const对象,其返回的迭代器应该具有允许访问(遍历)数据,但不允许修改数据的功能。而const iterator本质上修饰的是迭代器iterator本身,我们可以看作为char* const,这样的效果是不能改变迭代器的指向,但是可以改变迭代器指向数据的内容,着显然是不符合要求的。但是const_iterator本质上修饰的就是迭代器指向的数据,可以看作是const char*,符合要求。
//6、迭代器iterator--begin()/end()
void test_7()
{string s("hello world!");string::iterator it = s.begin();while (it != s.end()){(*it)++;cout << *it << " ";it++;}
}int main()
{test_7();return 0;
}
output:
i f m m p ! x p s m e "
1.1.5.2.2 rbegin()/rend()
reverse_iterator rbegin();
const_reverse_iterator rbegin() const;
######################################
reverse_iterator rend();
const_reverse_iterator rend() const;
和begin()/end()类似,只是返回的是反向迭代器,所谓反向迭代器即rbegin()返回一个指向字符序列最后一个有效字符的迭代器;rend()返回一个指向字符序列字符序列第一个字符之前的字符(被认为是反向末端)的迭代器。如果反向迭代器的指向可以修改,那么例如对于rbegin()的返回结果进行+1操作,就会使迭代器指向倒数第二个字符。
例如:
//7、rbegin()/end()
void test_8()
{string s("hello world!");string::reverse_iterator rit = s.rbegin();while (rit != s.rend()){cout << *rit << " ";++rit;}cout << endl;
}int main()
{test_8();return 0;
}
output:
! d l r o w o l l e h
1.1.6 string类对象的修改操作
1.1.6.1 增--插入(拼接):push_back/append/operator+=
void push_back (char c);
########################
string& append (const string& str);
string& append (const string& str, size_t subpos, size_t sublen);
string& append (const char* s);
string& append (const char* s, size_t n);
string& append (size_t n, char c);
##################################################################
string& operator+= (const string& str);
string& operator+= (const char* s);
string& operator+= (char c);
例如:
//8、插入(拼接)方式:push_back append operator+=
void test_9()
{string str;str.push_back(' ');//在str后插入空格str.append("hello");//在str后追加一个字符"hello"str += 'w';//在str后追加一个字符'b'str += "orld";//在str后追加一个字符串"it"cout << str << endl;
}int main()
{test_9();return 0;
}
output:
helloworld
1.1.6.2 删 erase
string& erase (size_t pos = 0, size_t len = npos);
即删除从pos位置开始的len个字符。
注意:npos为string里面定义的一个const静态全局变量,const static size_t npos=-1;
1、无符号整形npos的值为-1,因此它的实际值为unsigned int的最大值;
2、如果npos用来表示一个长度,那么它通常用来说明直到字符串的尾;
3、如果npos用来表示一个返回值,那么它通常用来说明没有找到目标。
例如:
//9、erase
void test_10()
{string str("hello world");str.erase(2, 2);cout << str << endl;str.erase(1);cout << str << endl;
}
output:
heo world
h
1.1.6.3 查find/rfind
size_t find (const string& str, size_t pos = 0) const;
size_t find (const char* s, size_t pos = 0) const;
size_t find (char c, size_t pos = 0) const;size_t rfind (const string& str, size_t pos = npos) const;
size_t rfind (const char* s, size_t pos = npos) const;
size_t rfind (char c, size_t pos = npos) const;
find:从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置。
rfind:从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置。
例如:
//10、正向和反向查找:find/rfind
void test_11()
{//获取file的后缀string file("string.cpp");size_t pos = file.rfind('.');string suffix(file.substr(pos, file.size() - pos));cout << suffix << endl;//取出url中的域名string url("https://legacy.cplusplus.com/reference/string/string/find/");cout << url << endl;size_t start = url.find("://");if (start == string::npos){cout << "invalid url" << endl;return;}start += 3;size_t finish = url.find('/', start);string address = url.substr(start, finish - start);cout << address << endl;//删除url的协议前缀pos = url.find("://");url.erase(0, pos + 3);cout << url << endl;
}int main()
{test_11();return 0;
}
output:
.cpp
https://legacy.cplusplus.com/reference/string/string/find/
legacy.cplusplus.com
legacy.cplusplus.com/reference/string/string/find/
1.1.6.4 改
string类中有专门用于修改字符串的函数replace,但是由于效率的原因并不常用。实际中,一般都是使用[]下标访问和迭代器访问来修改数据。
1.1.6.5 c_str--获得C语言类型的字符串
const char* c_str() const;
例如:
//11、c_str
void test_12()
{string s("hello world");const char* str = s.c_str();cout << str << endl;
}
output:
hello world
1.1.6.4 substr--获得子串
string substr (size_t pos = 0, size_t len = npos) const;
在str中从pos位置开始,截取len个字符,同时将这个子串存储到string类中并进行返回。
例如:
//12、substr
void test_13()
{string s1("hello world");string s2 = s1.substr(0, 5);//hellostring s3 = s1.substr(5);// worldcout << s2 << s3 << endl;
}int main()
{test_13();return 0;
}
output:
hello world
1.2 非成员函数
1.2.1 operator<< / operator>> --流插入/流提取运算符重载
1、有了<<流插入运算符重载,我们就可以利用std::cout向屏幕打印string存储的字符序列;
2、有了>>流提取运算符重载,我们就可以利用std::cin向string类中输入数据。
注意:1、 cin类似于C语言中的scanf,如果碰到空白字符就会停止读取,因此cin只能用于读取不带空格的字符序列。2、原来的数据会被输入端新字符给覆盖。3、如果输入的字符长度大于capacity,那么就会对这个string对象进行扩容,直到可以存储输入的字符序列。
例如:
//13、operator<</operator>>
void test_14()
{string s1, s2;cin >> s1;cin >> s2;cout << "s1 = " << s1 << endl;cout << "s2 = " << s2 << endl;
}
intput:
hello
world hello
output:
s1 = hello
s2 = world
1.2.2 getline--输入字符串
istream& getline (istream& is, string& str);
1、同样是从标准输入流向string对象输入数据;
2、原来的数据会被输入端新字符给覆盖;
3、getline()类似于C语言中的gets(),只有遇到换行才会停止读取。因此可以读取带空格的字符序列;
4、如果输入的字符长度大于capacity,那么就会对这个string对象进行扩容,直到可以存储输入的字符序列。
例如:
//14、getline()
void test_15()
{string s1;getline(cin, s1);cout << s1 << endl;
}
input:
hello world
output:
hello world
1.2.3 relational operators--关系运算符重载
(1)
bool operator== (const string& lhs, const string& rhs);
bool operator== (const char* lhs, const string& rhs);
bool operator== (const string& lhs, const char* rhs);(2)
bool operator!= (const string& lhs, const string& rhs);
bool operator!= (const char* lhs, const string& rhs);
bool operator!= (const string& lhs, const char* rhs);(3)
bool operator< (const string& lhs, const string& rhs);
bool operator< (const char* lhs, const string& rhs);
bool operator< (const string& lhs, const char* rhs);(4)
bool operator<= (const string& lhs, const string& rhs);
bool operator<= (const char* lhs, const string& rhs);
bool operator<= (const string& lhs, const char* rhs);(5)
bool operator> (const string& lhs, const string& rhs);
bool operator> (const char* lhs, const string& rhs);
bool operator> (const string& lhs, const char* rhs);(6)
bool operator>= (const string& lhs, const string& rhs);
bool operator>= (const char* lhs, const string& rhs);
bool operator>= (const string& lhs, const char* rhs);
C++string类关系运算符的逻辑与C语言字符串比较函数strcmp的逻辑类似。
string类的基本使用完整代码可参考:string类的基本使用
2.string类的简单模拟实现--MyString类
模拟实现的string类有4个成员变量:_str指向字符串的指针、_size字符串的大小、_capacity字符串容量、静态成员npos。
char* _str;
size_t _size;
size_t _capacity;
static const size_t npos = -1;
各个接口的实现:
2.1 构造函数
分配空间时,要多分配1个字节的空间用于存放'\0',有参构造和无参构造可以合并成半缺省。
//1、构造函数
MyString(const char*str=""):_size(strlen(str))//先_size后_capacity是为了匹配声明顺序,防止给成随机值
{_capacity = _size == 0 ? 2 : _size;_str = new char[_capacity + 1];//这里多开一个空间给结束符'\0'留空间strcpy(_str, str);
}
2.2 拷贝构造
2.2.1 拷贝构造的传统写法
//2、拷贝构造--string s2(s1)
//1)传统写法
MyString(const MyString& str):_str(new char[strlen(str._str) + 1]), _size(str._size), _capacity(str._capacity)
{strcpy(_str, str._str);
}
2.2.2 拷贝构造的常用写法
(1)先使用str._str作为参数构造一个临时对象;
(2)将临时对象tmp的内容和*this进行交换。
注意:必须要在初始化列表中将_str指针初始化为空指针,因为拷贝构造函数中创建的临时对象tmp出了拷贝构造函数的作用域时,会调用析构函数释放tmp在堆上申请的空间。如果_str没有被初始化为空指针,那么_str就是随机值,当tmp和_str进行交换操作后,tmp对象的成员_str也是随机值,随机值的空间是不能释放的,会导致不可预知的错误;空指针可以被释放,因此_str必须被初始化为空指针。
//2)拷贝构造现在常用写法
MyString(const MyString& str):_str(nullptr),_size(0),_capacity(0)
{MyString tmp(str._str);//使用构造函数创建一个临时对象swap(tmp);
}
2.3 swap
使用库里的swap函数交换*this和s的内容:包括_str字符串内容、_size字符串大小和_capacity字符串容量。
void swap(MyString& s)
{//用::指定调用全局的swap函数即库里的swap函数::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);
}
2.4 赋值运算符重载
2.4.1 写法1
//3、赋值运算符重载 s1=s2
//1)传统写法
MyString& operator=(const MyString& str)
{if (this != &str){char* tmp = new char[strlen(str._str) + 1];strcpy(tmp, str._str);delete[] _str;_str = tmp;_capacity = str._capacity;_size = str._size;}return *this;
}
2.4.2 写法2
使用swap()函数将*this的内容和s进行交换
//2)赋值的现代常用写法s1=s2
MyString operator=(MyString s)
{swap(s);return *this;
}
2.5 析构函数
释放_str在对上申请的空间、将_size和_capacity置0
~MyString()
{delete[] _str;_str = nullptr;_size = _capacity = 0;
}
2.6 返回字符串的_size和_capacity
//size()
size_t size() const
{return _size;
}size_t capacity() const
{return _capacity;
}
2.7 c_str()
获取c形式的字符串,将const string*类型转化为const char*类型。
//对象以字符串的形式返回
const char* c_str()
{return _str;
}
2.8 operator[]
分为普通operator[](可读可写)和const operator[](只读);如果要对_str中的字符进行修改的话,要返回引用;如果返回的是char,那么return就是传值返回,返回的是_str[i]的拷贝,即临时对象,而临时对象具有常性,不能被修改。
char& operator[](size_t i)
{assert(i < _size);return _str[i];
}//operator[]
const char& operator[](size_t i) const
{assert(i < _size);return _str[i];
}
2.9 push_back--字符串尾插
字符串尾插要考虑是否需要开辟空间。
//在字符串尾部插入一个字符
void push_back(const char ch)
{//首先判断字符串空间是否已满,如果满了则需要扩容if (_size == _capacity){size_t newCapacity = _capacity==0 ? 2 : 2 * _capacity;reserve(newCapacity);}_str[_size] = ch;++_size;_str[_size] = '\0';
}
2.10 append--尾插一个字符串
将字符串str中的内容拷贝到_str+_size(_str的末尾)位置 。
//尾插一个字符串
void append(const char* str)
{size_t len = strlen(str);if (_size + len > _capacity){reserve(len);}strcpy(_str + _size, str);_size += len;
}
2.11 operator+=()
分为两种情况:
(1)+=1个字符串,使用push_back插入;
(2)+=字符串,使用append追加到字符串末尾。
//s1+='a'
MyString& operator+=(const char ch)
{push_back(ch);return *this;
}//s1+="abcd"
MyString& operator+=(const char* s)
{append(s);return *this;
}
2.12 insert
在指定位置插入字符:(1)判断是否需要增容;(2)将pos位置至字符串末尾的字符都向后挪动一个位置;(3)将要插入的字符插入到pos位;(4)跟新_size。
//在指定位置插入一个字符,并返回新字符串
MyString& insert(size_t pos, const char ch)
{assert(pos <= _size);if (_size == _capacity){size_t newCapacity = _capacity == 0 ? 2 : 2 * _capacity;reserve(newCapacity);}//以下写法是错误的,为什么呢?/*size_t end = _size;while (end >= pos){_str[end + 1] = _str[end];--end;}*///为什么不能这样写呢?//当进行头插时,pos==0,此时end==0,执行到代码最后--end为-1,//由于end为无符号整型,故为-1时,即为size_t类型的最大值,此时会进入死循环。//那能不能把pos换成int类型?也不能,//pos为有符号int类型,end为无符号整型,两者进行比较时,pos会被提升为无符号整型,也进入死循环。size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];--end;}_str[pos] = ch;++_size;return *this;
}//在指定位置插入一个字符串
MyString& 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 (pos+len-1 < end){_str[end] = _str[end-len];--end;}strncpy(_str + pos, str,len);_size += len;return *this;
}
2.13 erase(size_t pos,size_t len=npos)
删除字符,len为要删除的字符的个数,分为两种情况:
(1)从pos到字符串末尾的字符个数小于len(即要删除的字符个数小于len),此时说明字符串从pos往后的长度不够删,此时pos之后的内容全部都被删完了,直接将pos位置赋值为'\0',_size更新为pos即可;
(2)从pos到字符串末尾的字符个数大于或者等于要删除的字符个数,此时剩余的字符需要向前挪动len位。
//erase()删除指定位置往后len个字符,并返回新字符串
MyString& erase(size_t pos, size_t len = npos)
{assert(pos < _size);if (len == npos || pos + len > _size){_str[pos] = '\0';_size = pos;}else{//写法1:/*size_t i = pos + len;while (i<=_size){_str[i-len] = _str[i];++i;}_size -= len;*///写法2:strcpy(_str + pos, _str + pos + len);_size -= len;}return *this;
}
2.14 reserve
开空间,扩展_capacity,_size不变
(1)申请新空间;(2)拷贝字符串;(3)释放旧空间;(4)指向新空间;(5)更新容量。
//将字符串容量扩展到n
void reserve(size_t n)
{if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}
}
2.15 resize(size_t n,char ch='\0'),
开空间+初始化,扩展_capacity,_size也要修改。
(1)当n<_size,无需扩容,此时_size调整为n;
(2)n>_size时,分两种情况:
①当_size<n<_capacity,无需增容,直接将_size到n位置的数据置为ch;
②当n>_capacity时,需要增容,并将_size到n位置的数据置为ch,_size也置为n。
n>_size的这两种情况只有是否会增容的区别,其他没有区别,可以合二为一。
//resize(size_t n,char ch='\0')
void resize(size_t n, char ch = '\0')
{if (n < _size){_str[_size] = '\0';_size = n;}else{if (n > _capacity){reserve(n);}for (int i = _size; i < n; ++i){_str[i] = ch;}_size = n;_str[_size] = '\0';}
}
2.16 find
size_t find (const char* s, size_t pos = 0) const;
size_t find (char c, size_t pos = 0) const;
即从pos位置开始,寻找目标出现的下标,分为查找字符和查找字符串,找到返回字符或者字符串第一个字符的下标,没找到返回0。
//find--查找字符size_t find(char ch, size_t pos = 0) const{assert(pos < _size);size_t i = pos;while (i < _size){if (_str[i] == ch){return i;}++i;}return npos;}//find--查找字符串size_t find(const char* str, size_t pos = 0) const{assert(pos < _size);char* p = strstr(_str+pos, str);if (p == nullptr){return npos;}else{return p - _str;}}
2.17 字符串比较
字符串比较,只需要实现<和==,剩余的就可以用这两个实现重载
//s1<s2
bool operator<(const MyString& s)
{return strcmp(_str, s._str) > 0;
}//s1==s2
bool operator==(const MyString& s)
{return strcmp(_str, s._str) == 0;
}//s1<=s2
bool operator<=(const MyString& s)
{return *this < s || *this == s;
}bool operator>(const MyString& s)
{return !(*this <= s);
}bool operator>=(const MyString& s)
{return !(*this < s);
}bool operator!=(const MyString& s)
{return !(*this == s);
}
2.18 清空字符串
//clear--将字符串清空
void clear()
{_str[0] = '\0';_size = 0;
}
2.19 输入/输出函数
//输出函数<<
ostream& operator<<(ostream& out, const MyString& str)
{for (int i = 0; i < str.size(); ++i){cout << str[i];}return out;
}//输入
istream& operator>>(istream& in, MyString& s)
{while (1){char ch;//in >> ch;ch = in.get();if (ch == ' ' || ch == '\n'){break;}else{s += ch;}}return in;
}
string类的简单实现的完整代码可参考:string类的简单实现。