文章目录
# 0 string
要使用string,要包含头文件。
#include <string>
string其实就是用来管理字符串的顺序表。
string的产生在数据库之前,并不划分到容器中,规范上来说和其他的容器也有一定差别,最主要的是冗余的代码太多。
由于其内部函数的重载太多,且部分功能重叠,下面都只是捡最常见常用的部分来写。
# 1 string的默认成员函数
文章只涉及构造,拷贝构造与赋值重载。
剩下三个都很简单,没什么好提的。
# 1.1 string的构造函数
string内部的构造函数有许多重载:
第二个显然就是拷贝构造。
最常用的是第一种,无参调用:
// 直接构造一个string类型的对象
string s1;
和第四种,用一个字符串来初始化构造:
// 这里s2和s3的两种写法本质上是一样的
// 二者都是构造
// 前者是构造很明显
// 后者是先用字符串构造一个临时的string,然后拷贝给s3
// 连续的构造+拷贝构造被优化成单构造
string s2 ("abcde");
string s3 = "abcde";
其它的都太少用了,所以不提了。
要求记得所有的接口是很不现实的,需要用的时候搜搜就好。
# 1.2 string的拷贝构造
就是上面那张图的第二个构造函数。
毕竟拷贝构造就是构造函数的一种重载嘛。
// 下面两种写法同样是一致的
// 都是单拷贝构造
string s4 = s3;
string s5(s4);
# 1.3 string的赋值重载
赋值重载也有多个重载:
// 对于两个已经存在的对象的拷贝,才是赋值重载
// 分别是用另一个string赋值
s1 = s5;
cout << s1 << endl;
// 用字符串赋值
s1 = "12345";
cout << s1 << endl;
// 用一个字符赋值
s1 = 'k';
cout << s1 << endl;
string中重载了流插入和流提取,所以可以直接用。
# 2 string的遍历与访问
大体上可以看作三种方法,
分别是熟悉的用下标和[ ]的组合;
迭代器;
以及范围for。
# 2.1 下标和[ ]
string内部实现了对[ ]的重载,允许了和以前访问顺序表一致的方式来遍历string。
void Test2()
{
string s1 = "abcdef";
cout << s1 << endl;
// 允许访问与修改
s1[0]++;
cout << s1 << endl;
cout << s1[2] << endl;
}
# 2.2 迭代器
关于迭代器的具体细节和实现方式,不同结构的迭代器各有不同。
对于string这样底层空间连续的结构来说,迭代器就是简单的指针。
void Test3()
{
string s1 = "123456";
string::iterator it = s1.begin();
while (it != s1.end())
{
cout << *it << endl;
++it;
}
}
# 2.3 范围for
范围for底层是用迭代器实现的,本质上是迭代器的简单替换
即虽然写法是范围for,但是编译器会直接将其替换为迭代器。
void Test3()
{
string s1 = "123456";
// 这是迭代器
//string::iterator it = s1.begin();
//while (it != s1.end())
//{
// cout << *it;
// ++it;
//}
//cout << endl;
// 这是范围for
for (auto e : s1)
{
cout << e;
}
cout << endl;
}
从编译器的角度来看,这两段代码完全一样。
# 3 string内部函数
首先,string的成员函数种类繁杂,每个函数都可能有不止一个的重载,
再有,部分重载乃至函数本身可能并没有实现的必要。
一一细说既效率低下,也没实际价值。
下面只介绍最常用的函数和部分重载。
# 3.1 size(),length(),capacity()
size()和length()的功能是一样的,无需参数,返回对象中数据的有效长度。
所谓有效长度,即不计算‘\0’。
capacity()同样无参,返回的是有效数据的最大长度。
这个容量指的并不是开出来的空间大小,会小上一个字节,
因为实际空间最后一个字节一定是‘\0’,这个字节不能算进有效数据。
void Test4()
{
string s1 = "qwerty";
cout << s1 << endl;
cout << s1.size() << endl;
cout << s1.length() << endl;
cout << s1.capacity() << endl;
}
# 3.2 push(),append(),+=重载
pus()只允许一个一个字符插入,想要插入字符串就要用append()。
+=重载是会根据参数是字符还是字符串,调用push()或是append()。
所以不用分辨字符和字符串,一律用+=是最简单的。
void Test5()
{
string s1 = "123456";
cout << s1 << endl;
s1.push_back('7');
s1.push_back('8');
s1.push_back('9');
cout << s1 << endl;
s1.append("0123");
cout << s1 << endl;
cout << endl;
string s2 = "123456";
cout << s2 << endl;
s2 += '7';
s2 += '8';
s2 += '9';
cout << s2 << endl;
s2 += "0123";
cout << s2 << endl;
}
# 3.3 reserve(),resize()
reserve()只改变capacity,不会对size和数据有任何改动。
resize()不仅可能改变capacity,还会改变size,以至于改变数据。
void Test6()
{
string s1 = "123456";
cout << s1.size() << ' ' << s1.capacity() << endl;
cout << s1 << endl << endl;
// 目标容量小于原容量
// 什么都不做
s1.reserve(10);
cout << s1.size() << ' ' << s1.capacity() << endl;
cout << s1 << endl << endl;
// 目标容量大于原容量
// 进行扩容
s1.reserve(20);
cout << s1.size() << ' ' << s1.capacity() << endl;
cout << s1 << endl << endl;
cout << endl;
string s2 = "123456";
cout << s2.size() << ' ' << s2.capacity() << endl;
cout << s2 << endl << endl;
// 目标size大于原size,小于容量
// 不扩容,用第二个参数将size填充至目标size
// 如果没有给第二个参数,有缺省值为'\0'
s2.resize(10, '7');
cout << s2.size() << ' ' << s2.capacity() << endl;
cout << s2 << endl << endl;
// 目标size大于容量
// 扩容,并填充
s2.resize(20,'8');
cout << s2.size() << ' ' << s2.capacity() << endl;
cout << s2 << endl << endl;
// 目标size小于原size
// 容量不变,删除数据至只剩下前目标size个
s2.resize(3);
cout << s2.size() << ' ' << s2.capacity() << endl;
cout << s2 << endl << endl;
}
# 3.4 find(),insert(),erase()
分别是查找,插入和删除。
find有两个参数,第一个是要寻找的字符或字符串,第二个是开始寻找的位置下标。
第二个参数有缺省值,为0,即从其实位置开始找。
插入也是两参数,第一个是目标插入位置下标,第二个是要插入的字符串。
插入数据过程中如果需要扩容,insert函数里面会自己调,不用操心。
删除两参数,第一个是目标删除位置下标。第二个是要删除的数据个数,或者说字节数。
第二个参数也有缺省值,为npos,就是从目标位置开始往后全删。
void Test7()
{
string s1 = "abcdefg";
// 找到了返回下标
cout << s1.find('a', 0) << endl;
// 找不到返回npos
cout << s1.find('a', 1) << endl;
cout<<endl;
// 从0位置插入,就是头插
s1.insert(0, "123");
cout << s1 << endl;
// 从size()位置插入,就是尾插
s1.insert(s1.size(), "456");
cout << s1 << endl;
// 从0位置,即从头删除两个字符
s1.erase(0, 2);
cout << s1 << endl;
// 从下标为3的位置删除1111个字符
// 这个数字过大,所以其实就是只保留前三个字符,后面全删
s1.erase(3, 1111);
cout << s1 << endl;
}