C++:渴望力量吗,少年?
文章目录
一、STL
1. 概念
STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。
此外,STL的版本不止一个,有由Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本,后面又有了P. J. 版本,RW版本,SGI版本……
SGI版本由Silicon Graphics Computer Systems,Inc公司开发,继承自HP版本。被GCC(Linux)采用,可移植性好,可公开、修改甚至贩卖,从命名风格和编程风格上看,阅读性非常高。我们后面学习STL要阅读部分源代码,主要参考的就是这个版本。
2. STL的六大组件
我们接下来要学的也是围绕这个图展开的。
3. STL的重要性
STL无论是在公司的C++笔试还是面试都是必考的知识。网上有句话说:“不懂STL,不要说你会C++”。STL是C++中的优秀作品,有了它的陪伴,许多底层的数据结构以及算法都不需要自己重新造轮子,站在前人的肩膀上,我们得以更好地使用C++进行开发。
二、string类的介绍与使用
1. 介绍
string是表示字符串的字符串类,大体结构和C语言的字符串差不多,但是不同的点在于这是一个容器,在这个容器之中提供了很多有用的接口,下面我们就是要来学习这些方法分别是什么以及如何使用。
注意:在使用string类时,必须包含头文件string以及using namespace std;
2. 使用
(1)string类对象的常见构造
(constructor)函数名称 | 功能说明 |
---|---|
string() (重点) | 构造空的string类对象,即空字符串 |
string(const char* s) (重点) | 用C-string来构造string类对象 |
string(size_t n, char c) | string类对象中包含n个字符c |
string(const string&s) (重点) | 拷贝构造函数 |
代码如下:
void Teststring()//先大概看一下
{
cout << "我是Teststring :" << endl;
string s1; // 构造空的string类对象s1
string s2("hello bit"); // 用C格式字符串构造string类对象s2
string s3(s2); // 拷贝构造s3
cin >> s1;
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;
}
void Test_string()
{
cout << "我是Test_string :" << endl;
string s1("hello worldxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyy");
string s2(s1);//拷贝构造
cout << s2 << endl;
string s3(s1, 6, 5);//在下标为6的位置开始取5个字符
cout << s3 << endl;
string s4(s1, 6, 3);
cout << s4 << endl;
//下面的这个函数重载和上面s3和s4是同一个,第三个参数是缺省参数npos,也就是size_t 的 -1
//当字符串长度小于第三个参数或者第三个参数不写时,就直接取到字符串末尾
string s5(s1, 6);
cout << s5 << endl;
string s6(s1, 6, s1.size() - 6);//等价于上一个,但是还是上面那个好用,因为不用自己计算
cout << s6 << endl;
string s7(10, 'a');//用10个a填充字符串
cout << s7 << endl;
string s8(++s7.begin(), --s7.end());//注意--end的位置所对应的字符并不会被取到(即左闭右开)
cout << s8 << endl;
string s9(10, 42);//注意第二个参数可以写数字,将被自动转换为ASCII为该数字所对应的字符
cout << s9 << endl;
}
int main()
{
Teststring();
Test_string();
return 0;
}
(2)string类对象的容量操作
函数名称 | 功能说明 |
---|---|
size(重点) | 返回字符串有效字符长度 |
length | 返回字符串有效字符长度 |
capacity | 返回已经分配的空间总大小 |
empty (重点) | 检测字符串是否为空串,是返回true,否则返回false |
clear (重点) | 清空有效字符 |
reserve (重点) | 请求更改capacity(可以为字符串预留空间) |
resize (重点) | 将有效字符的个数改成n个,多出的空间用字符c填充 |
注意:
A. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。
B. clear()只是将string中有效字符清空,不改变底层空间大小。
C. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用 ‘\0’ 来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。
值得注意的是,resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小(因为空间不够resize使用就得扩容)。如果是将元素个数减少,底层空间总大小不变。(即只扩容,不缩容)
D. reserve(size_t n=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小。
(3)string类对象的访问及遍历操作
函数名称 | 功能说明 |
---|---|
operator[ ] (重点) | 返回pos位置的字符 |
begin + end | begin获取一个字符的迭代器,end获取最后一个字符下一个位置的迭代器(也就是’\0’) |
rbegin + rend | rbegin获取最后一个字符(‘\0’)的前一个位置的迭代器,rend获取第一个字符的的前一个位置的迭代器 |
范围for | C++11支持更简洁的范围for的新遍历方式 |
(4)string类非成员函数
函数名称 | 功能说明 |
---|---|
operator+ | 尽量少用,因为传值返回,导致深拷贝效率低 |
operator>> (重点) | 输入运算符重载 |
operator<< (重点) | 输出运算符重载 |
getline (重点) | 获取一行字符串 |
代码如下:
void test_string1()
{
cout << "我是test_string1 :" << endl;
//下面两个本质上是调用构造函数
string s1;
string s2("hello");
cin >> s1;
cout << s1 << endl;
cout << s2 << endl;
// 比strcat效率高而且好用
string ret1 = s1 + s1;
cout << ret1 << endl;
string ret2 = s1 + "我来了";
cout << ret2 << endl;
}
void test_string2()
{
cout << "我是test_string2 :" << endl;
string s1("hello world");
string s2 = "hello world";//单参数的构造函数的隐式类型转换
// 遍历string
for (size_t i = 0; i < s1.size(); i++)
{
// 读
cout << s1[i] << " ";//重载了 [],不是数组访问,实际上是返回第i个下标的字符
}
cout << endl;
for (size_t i = 0; i < s1.size(); i++)
{
// 写
s1[i]++;//给s1的第i个下标的字符加1
}
cout << s1 << endl;
// 迭代器(暂时当作指针使用)
string::iterator it = s1.begin();
//while (it < s1.end()) // 这里可以用小于号但是不建议,因为这里是物理空间上连续才可以用,像list这种物理空间不连续的用不了
while (it != s1.end()) // 推荐玩法,通用
{//注意end是指向最后一个字符(斜杠零的上一个字符)的下一个,也就是刚好是斜杠零
// 读
cout << *it << " ";
++it;
}
cout << endl;
it = s1.begin();
while (it != s1.end())
{
// 写
*it = 'a';
++it;
}
cout << s1 << endl;
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
list<int>::iterator lit = lt.begin();
while (lit != lt.end())
{
cout << *lit << " ";
++lit;
}
cout << endl;
}//迭代器的好处在于屏蔽了底层的细节,都可以使用通用的方法来访问和修改容器
void test_string3()
{
cout << "我是test_string3 :" << endl;
string s1("hello world");
string::iterator it = s1.begin();
while (it != s1.end())
{
// 读
cout << *it << " ";
++it;
}
cout << endl;
//string::reverse_iterator rit = s1.rbegin();
auto rit = s1.rbegin();//rbegin指向了字符串的最后一个字符(也就是斜杠零的前一个)
while (rit != s1.rend())//rend指向了第一个字符的前一个字符的位置
{
cout << *rit << " ";
++rit;//注意虽然是++,但是这个迭代器是倒着走的
}//当然也可以用正向的迭代器,然后 --end 也可以达到同样的效果,但是没有这个好用
cout << endl;
// 原理:编译时编译器把范围for替换成迭代器
// 读
for (auto ch : s1)
{
cout << ch << " ";
}
cout << endl;
// 写
for (auto& ch : s1)
{
ch++;
}
cout << endl;
cout << s1 << endl;
}
void test_string4()
{
cout << "我是test_string4 :" << endl;
//string s1("hello worldxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyy");
string s1("hello world");
cout << s1.size() << endl;
cout << s1.length() << endl;//注意只有string有length,因为string出现的时间比stl早,当时用的是length,而后来的stl普遍用size
//所以现在string既可以用size也可以用length,但是其他的容器只有size
cout << s1.capacity() << endl;
s1.clear();//clear只清理数据,不释放空间,可以打印capacity查看(空间会由析构函数释放)
s1 += "张三";//一个汉字一般是两个字节
cout << s1 << endl;
cout << s1.size() << endl;
cout << s1.capacity() << endl;
}
int main()
{
test_string1();
test_string2();
test_string3();
test_string4();
return 0;
}
剩下的部分在下一篇 ~