赞
踩
我们可以看到在C++98中光构造函数就有7个重载,其中框住的三个是需要重点掌握的,下面分别演示一遍。
string();// 构造一个空字符串
string (const char* s);// 用C-string来构造string类对象
string (const char* s, size_t n);// 用C-string的前n个字符来构造string类对象
string (size_t n, char c);// 生成n个c字符的字符串
string (const string& str);// 利用原先的字符串做拷贝构造
// 拷贝str字符串中从pos位置开始的len个字符
string (const string& str, size_t pos, size_t len = npos);
string& operator= (const string& str);// 将一个string对象赋值给到另一个
string& operator= (const char* s);// 将一个字符串赋值给到string对象
string& operator= (char c);// 将一个字符赋值给到string对象
函数名称 | 功能说明 |
---|---|
size(重点) | 返回字符串有效字符长度 |
length | 返回字符串有效字符长度 |
capacity | 返回空间总大小 |
empty (重点) | 检测字符串释放为空串,是返回true,否则返回false |
clear (重点) | 清空有效字符 |
reserve (重点) | 为字符串预留空间 |
resize (重点) | 将有效字符的个数该成n个,多出的空间用字符c填充 |
下面我们来探究一下【capacity】
void TestPushBack()
{
string s;
size_t sz = s.capacity();
cout << "making s grow:\n";
for (int i = 0; i < 100; ++i)
{
s.push_back('c');
if (sz != s.capacity())
{
sz = s.capacity();
cout << "capacity changed: " << sz << '\n';
}
}
}
【clear】的作用就是将字符串清空将【size】改为0,那它还有什么细节呢?我们用下面的函数进行测试。
void Teststring1()
{
// 注意:string类对象支持直接用cin和cout进行输入和输出
string s("hello, bit!!!");
cout <<"clear前size:" << s.size() << endl;
//cout << s.length() << endl;
cout << "clear前capacity:" << s.capacity() << endl;
cout << s << endl;
// 将s中的字符串清空,注意清空时只是将size清0,不改变底层空间的大小
s.clear();
cout << "clear后size:" << s.size() << endl;
cout << "clear后capacity:" << s.capacity() << endl;
}
【empty】就是判断字符串是否为空,如果为空返回真(1),否则为假(0)。
【reserve】的功能是提前为一个字符串开好空间。
void resize (size_t n);
void resize (size_t n, char c); // 初始化数据为n个c字符
对于reserve来说只会改变capacity,size原来是多少就是多少不改变,但resize是二者都改变,size变成’n’,capacity遵循vs的扩容规则比n多一些。
第一点上面已经验证,第二点如图,我们发现会出现截断字符的现象。
s.resize(100, 'a');//指定填充的字符为a
函数名称 | 功能说明 |
---|---|
operator[] (重点) | 返回pos位置的字符,const string类对象调用 |
begin+ end | begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器 |
rbegin + rend | rbegin获取最后一个字符的迭代器 + rend获取第一个字符前一个位置的迭代器 |
范围for | C++11支持更简洁的范围for的新遍历方式 |
char& operator[] (size_t pos);
const char& operator[] (size_t pos) const;
这个接口学过运算符重载的肯定不陌生,本质上就是对[]
进行了重载,因此我们可以使用**下标加[]**的方式进行访问string中的数据。并且由于返回的是char类型的引用,因此我们可以直接通过下标加[]的方式进行修改数据。
在这里普通类型的会调用char& operator[] (size_t pos);,而const string s2会调用const类型的[]重载const char& operator[] (size_t pos) const;
void Teststring3()
{
string s1("hello Bit");
const string s2("Hello Bit");
cout << s1 << " " << s2 << endl;
cout << s1[0] << " " << s2[0] << endl;
s1[0] = 'H';
cout << s1 << endl;
// s2[0] = 'h'; 代码编译失败,因为const类型对象不能修改
}
下面我们要说以下迭代器,迭代器呢可以说是STL中很重要的一部分。简单来说迭代器就是用来遍历或访问容器中的数据的,我们暂时可以把迭代器想象成指针,通过指针的++或者–加解引用的方式,我们就可以遍历一个数组,或者访问数组中的元素。当然指针只是迭代器中的一种,迭代器要实现的目的就是通过++或者–能够遍历容器中所有的元素,我们数组是一段连续的空间,可以通过指针加一的方式遍历整个数组,但是如果是链表呢?这种情况下通过对每个指针++的操作就无法实现目的了,因此指针就不适合当迭代器了,我们就得封装新的迭代器。
在这之前呢,我们认识两个最常见的接口函数,即为begin和end
迭代器是是另一种访问string中内容的方式,图示如下,我们使用一个it去保存这个字符串【begin】处的位置,那么在其不断进行后移的过程中,就是在遍历这个字符串,当其到达最后的【end】处时,也就遍历完了,此刻便会停了下来。
void TestIterator()
{
string s("abcdef");
string::iterator it = s.begin();
while (it != s.end())
{
cout << *it << " ";
it++;
}
cout << endl;
}
void TestIterator() { string s("abcdef"); string::iterator it = s.begin(); while (it != s.end()) { (*it)++; it++; } it = s.begin(); while (it != s.end()) { cout << *it << endl; ++it; } }
begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器,而rbegin和rend恰恰相反,rbegin获取最后一个字符的迭代器 + rend获取第一个字符前一个位置的迭代器。
也就是说用begin和end遍历是从前往后遍历,用rbegin和rend就是从后往前遍历。
要注意的地方就是定义要这样string::reverse_iterator rit = s.rbegin();,当然如果记不住这么一长串我们可以直接使用auto来进行自动推导。
void Teststring4()
{
string s("abcdef");
string::reverse_iterator rit = s.rbegin();
// C++11之后,直接使用auto定义迭代器,让编译器推到迭代器的类型
//auto rit = s.rbegin();
while (rit != s.rend())
{
cout << *rit << endl;
rit++;
}
}
这里的auto我们以前介绍过,它可以自动推导类型,这里如果我们不用auto的话直接使用char也是可以的。
void TestRangeFor()
{
string s("abcdef");
for (auto ch : s)
{
cout << ch << " ";
}
cout << endl;
}
范围for的底层其实是迭代器,也就是在底层使用了迭代器进行遍历。如果没有迭代器,那就不能使用范围for。
也可以像迭代器那样在遍历的时候去做一个修改,值得注意的是要在ch后面加个引用,否则是达不到修改的目的的。
函数名称 | 功能说明 |
---|---|
push_back | 在字符串后尾插字符c |
append | 在字符串后追加一个字符串 |
operator+= (重点) | 在字符串后追加字符串str |
c_str(重点) | 返回C格式字符串 |
find + npos(重点) | 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置 |
rfind | 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置 |
substr | 在str中从pos位置开始,截取n个字符,然后将其返回 |
就是在当前的字符串后面追加一个字符,值得注意的是只能追加一个字符,不能追加多个字符否则会报错。
这个函数重载了很多,大家可以根据下面的介绍自己操作一下。
string& append (const string& str); // 追加一个string对象
// 追加一个string对象中的指定字符串长度
string& append (const string& str, size_t subpos, size_t sublen);
string& append (const char* s); // 追加一个字符串
string& append (const char* s, size_t n);// 追加字符串中的前n个字符串
string& append (size_t n, char c);// 追加n个字符
string s1("aaaaa"); string s2("bbbbb"); s1.append(s2); cout << s1 << endl; s1.append(" "); s1.append("ccccc"); cout << s1 << endl; s1.append(" "); s1.append("hello", 3); cout << s1 << endl; s1.append(" "); s1.append(10, 'e'); cout << s1 << endl;
自已对照一下。
s1 += s2;
s1+="world";
3. 拼接一个string类型加一个字符
s1+='w'
它的功能是返回一个c格式的字符串。有的人可能会说,为什么还要转换成C格式的字符串呢?那是因为我们调用某些函数,在传参的时候必须传的是c格式的字符串,不能传string,比如打开文件的fopen函数。
FILE * fopen ( const char * filename, const char * mode );
因此如果我们这样打开这个文件的话就会报错
void TestCStr()
{
string str = "test.cpp";
fopen(str, "r");
}
因此我们应该使用c_str函数进行转换,这个接口存在的意义就是一座桥,将C++和c关联起来。
FILE* fout = fopen(str.c_str(), "r");
operator+ | 尽量少用,因为传值返回,导致深拷贝效率低 |
operator>> (重点) | 输入运算符重载 |
operator<< (重点) | 输出运算符重载 |
getline (重点) | 获取一行字符串 |
relational operators (重点) | 大小比较 |
谈起operator+,不知道各位有没有想起operator+=呢?我们可以将二者对比一下。
二者的最本质区别还是在于这个效率问题,对于==operator+==而言,其底层在实现的时候因为无法对this指针本身造成修改,所以我们会通过拷贝构造出一个临时对象,对这个临时对象去做修改后返回,那我们知道返回一个出了作用域就销毁的对象,只能使用传值返回,此时又要发生一个拷贝因此本接口其实不太推荐读者使用,了解一下即可,尽量还是使用operator+= 来得好。
流提取,就是我们使用cin >>输入操作的时候一样,控制台要等待我们输入一个值
流插入,通过去缓冲区中拿取数据,然后将其显示在控制台上。
使用getline我们可以从缓冲区获取一整行的数据
无论是C语言中的scanf还是C++中的cin每次遇到空格就会直接结束输入,那有没有一种方法在输入的时候遇到空格不结束遇到换行才结束呢?那就是可以使用getline函数。
第一个参数填cin,第二个填要输入的字符串就行。
大小比较,这其实就是在底层将==、<、>等等这些运算符进行了重载。
==是将string中的每个字符进行对比,如果所有字符全都一样,那就返回true
<就是将两个字符串中的字符从第一个开始比大小,这里的比大小是按ASCII码进行比较,如果两个串s1和s2,其中s1第一个元素的ASCII码比s2第一个元素的ASCII码小那就返回true否则返回false并停止比较,如果s1和s2第一个字符相同,那就比较第二个字符,以此类推。
简单演示几个
由于ASCII码中b的比a的大,所以输出的是s1<s2。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。