STL标准模板库
STL(Standard Template Library,标准模板库)是C++标准库的一部分,提供了一系列通用的模板类和函数,这些模板类和函数可以用来实现常见的数据结构和算法。STL使得C++程序员能够更高效地编写代码,同时保持代码的可读性和可重用性。STL主要包括以下四个组件:
1.容器(Containers)
容器是用来管理某一类对象的集合。STL提供了多种类型的容器,每种容器都设计用来管理特定类型的对象集合。STL容器分为序列容器和关联容器两大类:
序列容器:维护元素的线性顺序。包括vector
、list
、deque
、string
、forward_list
等。
关联容器:基于关键字快速检索元素。包括set
、multiset
、map
、multimap
。
2.算法(Algorithms)
STL提供了一系列广泛使用的算法,如排序、搜索、变换和数值运算。这些算法被设计为与容器无关,可以作用于任何容器类型,只要容器满足算法的需求。常用的算法包括sort
、find
、copy
、accumulate
等。
3.迭代器(Iterators)
迭代器提供了一种访问容器中元素的方法,无需了解容器的内部结构。迭代器像一个指针,可以用来遍历STL中的容器。每种容器都提供了适用于其自身特性的迭代器。迭代器是连接容器与算法的桥梁。
4.适配器(Adapters)
适配器是一种用来修改容器或迭代器接口的工具,使其适应不同的需求。STL提供了几种容器适配器,如stack
、queue
、priority_queue
,它们分别实现了栈、队列和优先队列的功能。
String类的常见使用规则
C++标准库中的std::string
类是对字符串操作的封装,提供了一系列用于字符串处理的功能。std::string
类既灵活又易于使用,以下是std::string
类的一些常见使用规则和技巧:
初始化和赋值
利用构造函数初始化
std::string str; // 默认初始化为空字符串
std::string str(5, 'A'); // 创建一个包含5个 'A' 字符的字符串
std::string str(original, 0, 5); // 从原始字符串中提取前5个字符
std::string str(original.begin(), original.begin() + 5); // 从原始字符串中提取前5个字符
std::string copy(original); // 拷贝构造函数
std::string str2("World"); // 通过构造函数初始化
std::string str4(str1 + ", " + str2); // 通过连接两个字符串初始化
赋值运算符重载
std::string str1 = "Hello, World!"; // 从字符串字面值创建字符串对象
std::string str3 = str1; // 复制初始化
访问字符
char ch = str1[0]; // 访问第一个字符
char &ch_ref = str1[1]; // 获取字符的引用
ch_ref = 'a'; // 修改字符串中的字符
char ch_front = str1.front(); // 访问第一个字符
char ch_back = str1.back(); // 访问最后一个字符
字符串修改
str1 += " everyone"; // 追加字符串
str2.append("!!!"); // 使用append方法追加
str1.insert(5, " dear"); // 在指定位置插入字符串
str1.erase(1, 3); // 删除子字符串,从位置1开始,长度为3
str1.replace(0, 5, "Hi"); // 替换子字符串,从位置0开始,长度为5
字符串比较
if (str1 == str2) { /* ... */ } // 比较两个字符串是否相等
if (str1.compare(str2) == 0) { /* ... */ } // 使用compare方法比较
查找和子字符串
size_t pos = str1.find("lo"); // 查找子字符串的位置
if (pos != std::string::npos) { // 如果找到
std::string substr = str1.substr(pos); // 获取子字符串,pos到结尾(包括pos)
}
size_t pos = str1.find("lo",5); // 从下标5开始往后找,(包括下标5)查找子字符串的位置
if (pos != std::string::npos) { // 如果找到
std::string substr = str1.substr(pos,5); // 获取子字符串,pos往后的5个字符(包括pos)
}
字符串大小和清空
size_t len = str1.length(); // 或者 str1.size(); 获取字符串长度
str1.clear(); // 清空字符串
bool isEmpty = str1.empty(); // 检查字符串是否为空
字符串和数值之间的转换
int num = std::stoi("42"); // 字符串转整数
double d = std::stod("3.14"); // 字符串转浮点数
std::string strNum = std::to_string(42); // 整数转字符串
std::string strDouble = std::to_string(3.14); // 浮点数转字符串
C风格字符串
const char* cstr = str1.c_str(); // 获取C风格字符串
String类用法探究
size/clear/resize
#include <iostream>
using namespace std;
#include <string>
// 测试string容量相关的接口
// size/clear/resize
void Teststring1()
{
// 注意:string类对象支持直接用cin和cout进行输入和输出
string s("hello!!!!!!");
cout << s.size() << endl;
cout << s.length() << endl;
cout << s.capacity() << endl;
cout << s << endl;
// 将s中的字符串清空,注意清空时只是将size清0,不改变底层空间的大小
s.clear();
cout << s.size() << endl;
cout << s.capacity() << endl;
// 将s中有效字符个数增加到10个,多出位置用'a'进行填充
// “aaaaaaaaaa”
s.resize(10, 'a');
cout << s.size() << endl;
cout << s.capacity() << endl;
// 将s中有效字符个数增加到15个,多出位置用缺省值'\0'进行填充
// "aaaaaaaaaa\0\0\0\0\0"
// 注意此时s中有效字符个数已经增加到15个
s.resize(15);
cout << s.size() << endl;
cout << s.capacity() << endl;
cout << s << endl;
// 将s中有效字符个数缩小到5个
s.resize(5);
cout << s.size() << endl;
cout << s.capacity() << endl;
cout << s << endl;
}
int main(){
Teststring1();
}
在C++中,std::string
和STL容器(如std::vector
、std::list
等)提供了size
、clear
和resize
等成员函数,用于管理容器的大小和内容。这些函数的行为虽然在不同的容器中大致相似,但具体细节可能会有所不同。下面分别解释这三个函数的用法和作用:
size
size
函数返回容器中当前元素的数量。对于std::string
,size
返回的是字符串中字符的数量。
#include<string.h>
#include<vector>
#include<iostream>
int main(){
std::vector<int> vec = {1, 2, 3, 4, 5};
std::cout << "The size of the vector is: " << vec.size() << std::endl;
std::string str = "Hello, World!";
std::cout << "The size of the string is: " << str.size() << std::endl;
}
clear
clear
函数移除容器中的所有元素,将容器的大小变为0。对于std::string
,clear
会删除字符串中的所有字符,使其变成空字符串。
vec.clear(); // Now vec.size() == 0
str.clear(); // Now str.size() == 0, and str == ""
resize
resize
函数改变容器的大小。如果新的大小大于当前大小,新元素将被添加到容器的末尾,并初始化为默认值(对于std::string
,新字符默认为\0
)。如果新的大小小于当前大小,容器末尾的元素将被移除。
vec.resize(10); // 如果vec最初有5个元素,则会添加5个默认初始化的元素。
str.resize(5); // 如果str是“你好,世界!”,现在它是“你好”。
vec.resize(3); // 如果vec有10个元素,现在它有3个。
str.resize(2); // 如果str是“你好”,现在是“他”。
对于resize
函数,还可以指定新添加元素的值:
vec.resize(8, 100); // Vec大小调整为8。新元素初始化为100。
//对于std::string,指定新元素的值:
str.resize(10, 'x'); // Str大小调整为10。新字符用“x”初始化。
reserve
/*reserve使用规则探究*/
#include <iostream>
using namespace std;
#include <string>
void Teststring2()
{
string s;
// 测试reserve是否会改变string中有效元素个数
s.reserve(100);
cout << s.size() << endl;
cout << s.capacity() << endl;
// 测试reserve参数小于string的底层空间大小时,是否会将空间缩小
s.reserve(50);
cout << s.size() << endl;
cout << s.capacity() << endl;
}
int main(){
Teststring2();
}
// 利用reserve提高插入数据的效率,避免增容带来的开销
在C++标准模板库(STL)中,reserve
方法是用于容器(如std::vector
、std::string
等)的一个成员函数,它允许预先分配一定量的存储空间(capacity),以容纳特定数量的元素。使用reserve
可以提高容器在添加新元素时的效率,因为它减少了容器在元素被添加时可能发生的多次内存重新分配。
std::vector::reserve
对于std::vector
,reserve
方法预分配足够的内存空间以容纳指定数量的元素。如果参数大于当前的容量(capacity),则容器的容量将增加到该值。如果参数小于或等于当前的容量,则reserve
不会有任何效果,容器的容量不会减少。
std::vector<int> vec;
vec.reserve(100); // 预留空间以容纳至少100个int类型的元素
std::string::reserve
对于std::string
,reserve
同样用于预分配内存空间,以避免在追加字符时发生多次内存分配。
std::string str;
str.reserve(50); // 预留空间以容纳至少50个字符
小结论:
-
调用
reserve
并不改变容器中元素的数量(即size
属性),它只影响容器的容量(capacity
属性)。 -
如果容器的当前容量已经大于或等于
reserve
方法的参数值,通常情况下,容器的容量不会减少。 -
使用
reserve
可以提高性能,特别是在你知道将要在容器中存储大量元素时,预先分配足够的空间可以避免多次重新分配和复制元素。
尾插过程中capacity的变化
/*尾插的使用规则探究*/
#include <iostream>
using namespace std;
#include <string>
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';
}
}
}
int main(){
TestPushBack();
}
在C++中,std::string
类的尾部插入操作(如使用+=
操作符或append
方法添加字符)会影响字符串的容量(capacity
)。字符串的容量是指在需要重新分配内存之前,字符串可以存储的最大字符数(capacity
)。
尾插过程中的容量变化
当向std::string
对象尾部添加字符时,如果添加的字符数量没有超过当前的容量,那么字符串可以在不重新分配内存的情况下存储这些字符。但是,如果需要存储的字符数量超过了当前的容量,std::string
就需要增加其容量来容纳更多的字符。这时,std::string
通常会执行以下步骤:
-
重新分配内存:分配一个更大的内存块来存储现有的字符以及新添加的字符。新容量的具体大小取决于实现的增长策略,但通常是当前容量的两倍或当前容量加上一个固定的增量,以此来平衡内存使用和性能。
-
复制现有字符:将现有的字符从旧内存位置复制到新分配的内存位置。
-
添加新字符:在新内存的末尾插入新字符。
-
释放旧内存:释放原来存储字符串的内存块。
尾插过程中的优化
/*尾插的优化探究*/
#include <iostream>
using namespace std;
#include <string>
// 构建vector时,如果提前已经知道string中大概要放多少个元素,可以提前将string中空间设置好
void TestPushBackReserve()
{
string s;
s.reserve(100);
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';
}
}
}
int main(){
TestPushBackReserve();
}
为了减少因尾部插入操作导致的内存重新分配次数,可以使用reserve
方法预先分配足够的容量。这在你知道将要添加的字符数量或者想要最小化内存分配次数时非常有用。
string的打印、访问与修改
/*string的打印与访问*/
#include <iostream>
using namespace std;
#include <string>
void Teststring3()
{
string s1("hello world");
const string s2("Hello world");
cout << s1 << " " << s2 << endl;
cout << s1[0] << " " << s2[0] << endl;
s1[0] = 'H';
cout << s1 << endl;
// s2[0] = 'h'; 代码编译失败,因为const类型对象不能修改
}
int main(){
Teststring3();
}
string的遍历
/*string的遍历*/
/*string的遍历*/
#include <iostream>
using namespace std;
#include <string>
void Teststring4() {
string s("hello world");
// 3种遍历方式:
// 需要注意的以下三种方式除了遍历string对象,还可以遍历是修改string中的字符,
// 另外以下三种方式对于string而言,第一种使用最多
// 1. for+operator[]
for (size_t i = 0; i < s.size(); ++i)
cout << s[i];
// 2.迭代器
cout << endl;
string::iterator it = s.begin();
while (it != s.end()) {
cout << *it ;
++it;
}
// string::reverse_iterator rit = s.rbegin();
// C++11之后,直接使用auto定义迭代器,让编译器推到迭代器的类型
cout << endl;
auto rit = s.rbegin();
while (rit != s.rend()) {
cout << *rit ;
++rit;
}
// 3.范围for
cout << endl;
for (auto ch : s)
cout << ch ;
}
int main() {
Teststring4();
}
string的插入,查找,子串与删除
// 测试string:
// 1. 插入(拼接)方式:push_back append operator+=
// 2. 正向和反向查找:find() + rfind()
// 3. 截取子串:substr()
// 4. 删除:erase
#include <iostream>
using namespace std;
#include <string>
void Teststring5()
{
string str;
str.push_back(' '); // 在str后插入空格
str.append("hello"); // 在str后追加一个字符"hello"
str += 'w'; // 在str后追加一个字符'b'
str += "orld"; // 在str后追加一个字符串"orld"
cout << str << endl;
cout << str.c_str() << endl; // 以C语言的方式打印字符串
// 获取file的后缀
string file("string.cpp");
size_t pos = file.rfind('.');
string suffix(file.substr(pos, file.size() - pos));
cout << suffix << endl;
// npos是string里面的一个静态成员变量
// static const size_t npos = -1;
// 取出url中的域名
string url("http://www.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()
{
Teststring5();
return 0;
}
插入:push_back append operator+=
在C++的std::string
类中,push_back
、append
和operator+=
都是用来向字符串末尾添加内容的成员函数,但它们在使用上有细微的差别。
push_back
push_back
函数用于向字符串的末尾添加一个字符。
它只能添加单个字符。
std::string str = "Hello";
str.push_back('!');
std::cout << str; // 输出: Hello!
append
append
函数比push_back
更灵活,它可以用多种方式向字符串末尾添加字符串。
它可以添加一个C风格的字符串、另一个std::string
的一部分,或者多个重复的字符。
std::string str = "Hello";
str.append(" World"); // 添加另一个字符串
std::cout << str; // 输出: Hello World
std::string str2 = "123456";
str.append(str2, 2, 3); // 从str2的索引2开始,添加长度为3的子串
std::cout << str; // 输出: Hello World345
str.append(3, '!'); // 添加3个'!'字符
std::cout << str; // 输出: Hello World345!!!
operator+=
operator+=
是一个重载的操作符,用于向字符串的末尾添加内容。
它既可以添加单个字符,也可以添加另一个字符串,使用起来非常方便。
std::string str = "Hello";
str += ", World"; // 添加另一个字符串
std::cout << str; // 输出: Hello, World
str += '!'; // 添加单个字符
std::cout << str; // 输出: Hello, World!
查找:find() + rfind()
在C++的std::string
类中,find()
和rfind()
函数用于在字符串中查找子字符串或字符的位置。它们的主要区别在于搜索的方向:find()
从字符串的开头向末尾搜索,而rfind()
从字符串的末尾向开头搜索。
find()
size_t find(const std::string& str, size_t pos = 0);
在字符串中查找子字符串 str
,从索引位置 pos
开始查找,返回第一次出现的位置的索引(从0开始计数)。
如果未找到子字符串,返回特殊值 std::string::npos
。
std::string str = "Hello, world! Hello, everyone!";
size_t pos = str.find("Hello");
if (pos != std::string::npos) {
std::cout << "Found 'Hello' at position: " << pos << std::endl;
} else {
std::cout << "'Hello' not found" << std::endl;
}
rfind()
size_t rfind(const std::string& str, size_t pos = npos);
从字符串的末尾开始查找子字符串 str
,从索引位置 pos
开始查找,返回最后一次出现的位置的索引(从0开始计数)。
如果未找到子字符串,返回特殊值 std::string::npos
。
size_t rpos = str.rfind("Hello");
if (rpos != std::string::npos) {
std::cout << "Found the last 'Hello' at position: " << rpos << std::endl;
} else {
std::cout << "Last 'Hello' not found" << std::endl;
}
子串:substr()
在C++中,std::string
类提供的substr()
成员函数用于从字符串中提取一个子字符串。这个函数非常有用,尤其是在需要对字符串进行分割或提取特定部分时。
std::string substr(size_t pos = 0, size_t len = npos) const;
-
pos
参数指定子字符串开始的位置(索引从0开始)。 -
len
参数指定子字符串的长度。如果len
超过了pos
开始到字符串末尾的长度,则返回的子字符串会从pos
开始一直到字符串的末尾。 -
如果不提供
len
参数,子字符串将包含从pos
开始到原字符串末尾的所有字符。 -
npos
是std::string
中定义的一个常量,表示最大可能的字符串长度,通常用作默认值。
std::string str = "Hello, world!";
// 提取从位置7开始的5个字符
std::string sub1 = str.substr(7, 5);
std::cout << sub1 << std::endl; // 输出: world
// 从位置7开始到字符串末尾的所有字符
std::string sub2 = str.substr(7);
std::cout << sub2 << std::endl; // 输出: world!
// 从开始到字符串末尾的所有字符(即复制整个字符串)
std::string sub3 = str.substr();
std::cout << sub3 << std::endl; // 输出: Hello, world!
删除:erase()
在C++的std::string
类中,erase()
成员函数用于从字符串中删除指定的字符或子字符串。这个函数提供了几种不同的重载版本,允许以多种方式来指定要删除的内容。
删除指定位置的字符:
string& erase(size_t pos = 0, size_t len = npos);
pos
参数指定开始删除的位置(从0开始计数)。
len
参数指定要删除的字符数。如果len
是std::string::npos
(默认值),则表示删除从pos
开始到字符串末尾的所有字符
删除指定迭代器位置的字符:
iterator erase(iterator position);
position
是指向要删除字符的迭代器。
删除指定迭代器范围内的字符:
iterator erase(iterator first, iterator last);
first
和last
分别是指向要删除范围的起始和结束位置的迭代器。此范围包括first
指向的字符,但不包括last
指向的字符。
std::string str = "Hello, world!";
// 删除从位置7开始的5个字符(即"world")
str.erase(7, 5);
std::cout << str << std::endl; // 输出: Hello, !
// 删除指定位置的单个字符
auto it = str.begin() + 5; // 指向','
str.erase(it);
std::cout << str << std::endl; // 输出: Hello !
// 删除指定迭代器范围内的所有字符
str.erase(str.begin() + 5, str.end());
std::cout << str << std::endl; // 输出: Hello
结尾
最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。
同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。
谢谢您的支持,期待与您在下一篇文章中再次相遇!