目录
1. 相对于C语言,我们为什么还需要学习C++的string?
一,STL
1. 简介
STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。
2. STL的版本
3. STL 六大组件
4. 学习STL, 三境界
1. 熟练使用 STL。
2. 能够比较清晰的认识STL的底层原理。
3. 能够编写STL的扩展。(在校期间,少数能掌握)
5. 学会查看C++文档
Reference - C++ Reference (cplusplus.com)
学习中光string类就有100来个接口,我们熟练掌握常见20个,其余80来个需要我们查找文档,而最好的方式是查看英文原文档。
二, string类
1. 相对于C语言,我们为什么还需要学习C++的string?
2. 头文件
在使用string类时,必须包含头文件以及using namespace std;
#include <string>
3. 常见构造函数
常见的string类构造函数及功能:
这是原文:
选择 constructor (函数)进入:
即可见
建议:尝试去阅读原文档,对函数功能的解释。
演示如下:
int main()
{
// string(const char* s) (重点) 用string来构造string类对象
string s1("hello, C++");
cout << "s1 :" << s1 << endl;
// string() (重点) 构造空的string类对象,即空字符串
string s2;
cout << "s2 :" << s2;
cin >> s2;
cout << "s2 :" << s2 << endl;
// string(const string & s) (重点) 拷贝构造函数
string s3(s1);
cout << "s3 :" << s3 << endl;
// string(size_t n, char c) string类对象中包含n个字符c
string s4(4, 'c');
cout << "s4 :" << s4 << endl;
return 0;
}
这里单独对第三个函数 子链函数 进行解释:
4. operator=
成员函数:
功能展示:
int main()
{
string s2;
// string & operator= (const char* s);
string s1 = "hello, C++"; // 构造 + 拷贝 -> 优化为一次构造
cout << "s1: " << s1 << endl; // 结果: hello, C++
// string& operator= (const string & str);
s2 = s1;
cout << "s2: " << s2 << endl; // 结果: hello, C++
// string& operator= (char c);
s2 = 'x';
cout << "s2: " << s2 << endl; // 结果: x
return 0;
}
补充: assign 函数 跟赋值差不多,感兴趣可以去查查文档,这里不进行举出。
5. operator[] && at函数
功能描述: 两者都是使string类像数组一样的访问字符串。
成员函数:
区别在返回值
operator[] 在越界时会发生 断言 ;at 访问越界则 返回 异常。
功能展示:
int main()
{
// 1. char& operator[] (size_t pos); // 返回可修改的别名
string s1 = "hello, C++";
cout << s1[4] << endl; // at : s1.at(4)
s1[4] = 'k'; // 如果类对象实例化时,未被const修饰,同时也具有修改的能力
// 2. const char& operator[] (size_t pos) const;
const string s2 = s1;
cout << s2[4] << endl;
s2[4] = 'o'; // s2 被const修饰,不允许修改其内容
return 0;
}
6. string容量方面
1. 关于 size 与 length 的选择
两者功能都是获取容器中字符串长度
一般推荐使用 size() ,因为 length出道比较早,size是后期出道,string类中使用 size 比较多。(注意: max_size 这个没什么意义,返回的都是41亿)
简单遍历输出一遍字符串:
int main()
{
string s1 = "hello, C++";
for (int i = 0; i < s1.size() ; i++)
{
cout << s1[i]; // at: s1.at(i);
}
return 0;
}
2. 关于string类 的扩容机制
结论: 在不同编译器下扩容机制可能不同,有的是1.5倍,而有的是2倍的扩容。(STL标准库,只是一种标准,不同的编译器厂商有着自己的功能实现方法)
这里用同一段代码在 linux g++和 VS2019 下 扩容的结果:
3. 设置容量
我们知道频繁扩容,会有额外的消耗,于是我们预先设定一定大小的容量,减少扩容次数。
函数:
简单使用:
s1.reserve(1000); // 单纯开空间
s2.resize(1000); // 开空间, 同时设置字符,默认为‘0’
s2.resize(1000, 'c');
7. iterators——迭代器(重要)
迭代器是string 中的内部类类型,功能跟 operator[]差不多,像数组一样的访问数据。但字符串底层是数组,是一段连续的空间,用operator[] 会比较便利。后面我们学习 STL的vector(顺序表), list(链表)后,我们会发现,operator[]在连续存储的数据结构访问还行,但链表,树,迭代器更合适,而且他们用法类似,因此我们同时学习迭代器。(迭代器是容器通用访问方式,用法类似,提前上路)
简单用法:
int main()
{
string s1 = "hello, C++";
string::iterator it = s1.begin();
while (it != s1.end()) // != 原因:1. string类本身有关 2.其他容器也是用 != 这样设计统一了
{
cout << *it;
it++;
}
return 0;
}
s1.begin() 和 s1.end() 返回迭代器的位置是 [左闭区间 右开区间)
1. 回望 C++入门的 范围 for (语法糖 for)
int main()
{
string s1 = "hello, C++";
for ( auto e : s1) // 自动++, 自动判断结束
{
cout << e;
}
return 0;
}
语法糖 底层是通过 迭代器实现,且只能正向遍历。
2. 反向迭代
功能:反向遍历数据
简单用法:
int main()
{
string s1 = "hello, C++";
string::reverse_iterator it = s1.rbegin();
while ( it != s1.rend())
{
cout << *it;
it++; // 底层肯定是反向++
}
return 0;
}
当然 不管是 s1.begin ,s1.end; s1.rbegin, s2.rend都有返回 const 修饰的迭代器重载函数(一般发生在 传string引用时,如: const string& s1)
8. 字符串插入
C语言中,库函数strcat 也有这样的功能,但没有C++这么智能,C语言劣势:
- 1. 每次插入字符,字符串需要遍历整个字符串,效率低,且需要保证被插入的字符数组足够大。
- 2. C++的插入会记录末尾字符下标,可以做到直接插入。
1. 常见的插入方式:
1. push_back
2. append
3. operator +=
详细查文档
简答操作:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("hello, C++");
s1.push_back('6');
s1.push_back('-');
s1.push_back('-');
cout << s1 << endl;
s1.append("5");
cout << s1 << endl;
string s2(s1);
s2 += s1;
s2 += 'c';
s2 += "你好";
cout << s2 << endl;
return 0;
}
2. 关于中间插入 insert的使用
结论: 尽量少用,string类,insert 中间插入,会导致后面字符挪位,假设要头插入n个字符,第2次,挪一次; 第3次,挪2次;第n次,挪 n - 1次;所以挪位时间复杂度为n2了,所以尽量少用。
常见的三个insert 的使用:
运用如下:
int main()
{
// 1. 迭代器
string s1 = "hello, C++";
string::iterator it = s1.begin() + 2; // 也就是在下标为2的位置插入
s1.insert(it, 'Z');
cout << s1 << endl; // heZllo, C++
string s2 = s1;
s2.insert(2, "你好"); // 下标为2的位置
cout << s2 << endl;
string s3 = s1;
s3.insert(5, s2); // 下标为5的位置插入
cout << s3 << endl;
return 0;
}
9. 删除字符串——erase
函数: erase
代码实践:
int main()
{
// 1. 迭代器
string s1 = "hello, C++";
string::iterator it = s1.begin() + 2; // 也就是在下标为2的位置插入
s1.insert(it, 'Z');
cout << s1 << endl; // heZllo, C++
string s2 = s1;
s2.insert(2, "你好"); // 下标为2的位置
cout << s2 << endl;
string s3 = s1;
s3.insert(5, s2); // 下标为5的位置插入
cout << s3 << endl;
// 1. 迭代器删除
//s3.erase(2);
string::iterator it2 = s3.begin() + 2;
string::iterator it3 = s3.end();
s3.erase(it2, it3);
cout << s3 << endl;
return 0;
}
10. c_str
1. 使用场景
在一些需要用到C语言接口时,C语言不支持C++的string类,而c_str 成员函数可以将string类字符串转化为C语言兼容的字符串。
如:用C语言访问文件夹
string s1("Test.cpp");
const char* file = s1.c_str();
FILE* myfile = fopen( file, "r"); // 不认string类
char i = fgetc(myfile);
while ( i != EOF)
{
cout << i;
i = fgetc(myfile);
}
2. ‘\0’在 C语言 & C++string类中的区别
结论:
C语言比较重要: ‘\0’在C语言中,像strcopy, strcat等等字符串接口函数结束大多有 '\0' 作为字符串结尾;
C++直接无视: 我们可以将字符串中间任意位置设置为\0,但是在C++中字符串会将\0当成一个正常字符处理(打印时会当成空格或者不占位置的字符输出)
int main()
{
string s1("test.cpp");
s1 += '\0';
s1 += '\0';
s1 += '\0';
s1 += '\0';
s1 += "test.cpp";
cout << s1.c_str() << endl; // ‘\0’对于C语言,是字符串结束的标志
cout << s1 << endl; // 而在string中,字符串中可以存在,但不发生显示或者是空格。
return 0;
}
11. find & rfind
pos: 开始查找的字符位
n: 匹配的字符序列的长度
1.获取文件名后缀
int main()
{ // 1. 简单文件获取后缀
string s1("test.cpp");
size_t i = s1.find('.');
string tmp;
if (i != string::npos)
{
tmp = s1.substr(i); // 先用获取子串函数
}
cout << tmp << endl;
// 2. 复杂后缀,而只取最后的后缀
string s2("test.cpp.tar.zip");
size_t i2 = s2.rfind('.'); // 从后向前寻找
string tmp2;
if (i2 != string::npos)
{
tmp2 = s2.substr(i2);
}
cout << tmp2;
return 0;
}
补充一下substr:
2. 分离网址
int main()
{
string s1("https://mp.csdn.net/mp_blog/creation/editor/131103529");
// 1. 协议
size_t pl = s1.find("://");
size_t next = pl;
string protocol = s1.substr(0, pl);
cout << protocol << endl;
// 2. 域名
size_t p2 = s1.find("/", next += 3);
string rn = s1.substr(next, p2 - next);
next = p2;
cout << rn << endl;
// 3. 资源名
string way = s1.substr(next);
cout << way << endl;
return 0;
}
12. getline
功能:用于从输入流中读取一行数据,并存储到string对象中。
is:输入流对象。
str:用于存储读取的数据的string对象。
delim:可选参数,表示行结束符,默认为'\n'。(没有空格作为结束符)
比如牛客这道题:
字符串最后一个单词的长度_牛客题霸_牛客网 (nowcoder.com)
13. 字符串 互相转化 其他数据(C++11)
这个感兴趣可以一个个试。举一个栗子:
stoul : 字符串 转 无符号长整型
关于string类使用总结
1. 学会查文档!!!(重要)
2. 可以问chatgpt
3. 多练则会
结语
本小节就到这里了,感谢小伙伴的浏览,如果有什么建议,欢迎在评论区评论;如果给小伙伴带来一些收获请留下你的小赞,你的点赞和关注将会成为博主创作的动力。