cpp容器——string
·
string介绍
string也是cpp中的一个容器,从名字可以看出来它封装的是和字符串相关的操作, 它是basic_string的一个类型别名,类型是std::basic_string<char>,让我们看一下basic_string的定义,它接受三个模板参数,其余两个模板参数使用默认值

string是序列式容器
成员类型
- 包括指针、迭代器、值类型等
- 还有一个值
npos,它是size_t(-1), 用于比较string的某些函数的返回值的结果是否正常

构造函数
string的构造函数比较乱,我挑几个常用的进行介绍,包括构造函数等,这里就不介绍析构函数了

- 示例
#include <iostream>
#include <string>
int main() {
// 1. 默认构造
std::string s1;
std::cout << "s1: \"" << s1 << "\"\n";
// 2. 从 C 字符串构造
std::string s2("hello");
std::cout << "s2: " << s2 << "\n";
// 3. 拷贝构造
std::string s3(s2);
std::cout << "s3: " << s3 << "\n";
// 4. 子串构造(pos, len)
std::string s4(s2, 1, 3);
std::cout << "s4: " << s4 << "\n";
// 5. 重复字符构造(count, char)
std::string s5(5, 'a');
std::cout << "s5: " << s5 << "\n";
// 6. 从 char 数组 + 指定长度
const char arr[] = {'h', 'e', 'l', 'l', 'o', '\0', 'x'};
std::string s6(arr, 5);
std::cout << "s6: " << s6 << "\n";
// 7. 迭代器区间构造
std::string s7(s2.begin(), s2.end());
std::cout << "s7: " << s7 << "\n";
// 8. 移动构造(C++11)
std::string temp = "temporary";
std::string s8(std::move(temp));
std::cout << "s8: " << s8 << "\n";
std::cout << "temp(after move): " << temp << "\n";
// 9. initializer_list 构造
std::string s9({'H', 'i', '!'});
std::cout << "s9: " << s9 << "\n";
// 10. assign(不是构造,但常一起用)
std::string s10;
s10.assign("world");
std::cout << "s10: " << s10 << "\n";
return 0;
}
成员函数
元素访问
at访问指定索引的元素前会检查是否越界,越界就会抛异常,返回对应元素的引用;operator[]的行为和at一样,只不过不会检查索引是否有效;front返回首个有效的元素;back返回最后一个有效的元素;和vector一样,string底层也是使用动态数组实现的,所以data返回存储数据的原始指针;c_str返回一个以‘\0’结尾的字符数组

- 代码示例
#include <iostream>
#include <string>
int main() {
std::string s = "hello";
// 1. at()(带边界检查)
std::cout << "at(1): " << s.at(1) << "\n";
// 2. operator[]
std::cout << "operator[2]: " << s[2] << "\n";
// 3. front()
std::cout << "front(): " << s.front() << "\n";
// 4. back()
std::cout << "back(): " << s.back() << "\n";
// 5. data()
const char* p1 = s.data();
std::cout << "data(): " << p1 << "\n";
// 6. c_str()
const char* p2 = s.c_str();
std::cout << "c_str(): " << p2 << "\n";
// 修改内容(验证访问)
s[0] = 'H';
std::cout << "after modify: " << s << "\n";
// 使用 at 修改
s.at(1) = 'A';
std::cout << "after at modify: " << s << "\n";
return 0;
}
迭代器
begin()获取首个有效元素的地址,end获取最后一个有效元素的下一个位置,以及反向迭代器,还有一些常量迭代器,适合于常量对象使用

- 代码示例
#include <iostream>
#include <string>
int main() {
std::string s = "hello";
// 1. begin() / end()
std::cout << "forward (begin -> end): ";
for (auto it = s.begin(); it != s.end(); ++it) {
std::cout << *it << " ";
}
std::cout << "\n";
// 2. cbegin() / cend()(只读迭代器)
std::cout << "const forward (cbegin -> cend): ";
for (auto it = s.cbegin(); it != s.cend(); ++it) {
std::cout << *it << " ";
// *it = 'x'; // ❌ 编译错误(只读)
}
std::cout << "\n";
// 3. rbegin() / rend()(反向迭代器)
std::cout << "reverse (rbegin -> rend): ";
for (auto it = s.rbegin(); it != s.rend(); ++it) {
std::cout << *it << " ";
}
std::cout << "\n";
// 4. crbegin() / crend()(只读反向)
std::cout << "const reverse (crbegin -> crend): ";
for (auto it = s.crbegin(); it != s.crend(); ++it) {
std::cout << *it << " ";
}
std::cout << "\n";
// 5. 通过迭代器修改内容
for (auto it = s.begin(); it != s.end(); ++it) {
*it = std::toupper(*it);
}
std::cout << "after modify: " << s << "\n";
return 0;
}
容量相关
empty判断容器是否有有效的元素;size和length计算容器内的有效元素个数;max_size得到可以容纳的最大元素数量;reserve预分配空间;capacity获取元素的容量;shrink_to_fit可以把容器的capacity减少到和size一样,但是具体取决于编译器实现,不一定会缩容,使用时需要注意

#include <iostream>
#include <string>
int main() {
std::string s;
// 1. empty()
std::cout << "empty: " << std::boolalpha << s.empty() << "\n";
// 2. size() / length()
s = "hello";
std::cout << "size: " << s.size() << "\n";
std::cout << "length: " << s.length() << "\n";
// 3. capacity()
std::cout << "capacity(before): " << s.capacity() << "\n";
// 4. reserve()
s.reserve(100);
std::cout << "capacity(after reserve 100): " << s.capacity() << "\n";
// 5. max_size()
std::cout << "max_size: " << s.max_size() << "\n";
// 6. 扩展字符串,观察 capacity 变化
for (int i = 0; i < 50; ++i) {
s.push_back('x');
}
std::cout << "size(after push): " << s.size() << "\n";
std::cout << "capacity(after push): " << s.capacity() << "\n";
// 7. shrink_to_fit()
s.shrink_to_fit();
std::cout << "capacity(after shrink): " << s.capacity() << "\n";
return 0;
}
修改操作
clear函数只会清空元素,不会改变数量;insert可以在指定位置插入一个或一段区间的元素,返回插入的第一个元素的迭代器;push_back在容器末尾插入一个元素;pop_back移除末尾的元素;resize可以重新指定容器的大小,多余的元素以对应类型的默认值填充或者指定值填充;swap可以用来交换两个容器的内容;append可以直接在容器末尾追加元素;operatpr+=可以让容器和字符串做加法或者直接和相同类型的对象相加;replace用给定的字符串替换指定区间的元素;copy用来拷贝一个指定区间的字符串传递给一个变量,返回拷贝的字符数量,其余函数需要的cpp标准比较高,这里就不介绍了

- 代码示例
#include <iostream>
#include <string>
#include <algorithm>
#include <cstring>
int main() {
std::string s = "hello";
// 1. clear()
{
std::string t = s;
t.clear();
std::cout << "clear: \"" << t << "\"\n";
}
// 2. insert()
{
std::string t = s;
t.insert(2, "XYZ");
std::cout << "insert: " << t << "\n";
}
// 3. erase()
{
std::string t = s;
t.erase(1, 2);
std::cout << "erase: " << t << "\n";
}
// 4. push_back()
{
std::string t = s;
t.push_back('!');
std::cout << "push_back: " << t << "\n";
}
// 5. pop_back()
{
std::string t = s;
t.pop_back();
std::cout << "pop_back: " << t << "\n";
}
// 6. append()
{
std::string t = s;
t.append(" world");
std::cout << "append: " << t << "\n";
}
// 7. operator+=
{
std::string t = s;
t += "!!!";
std::cout << "operator+=: " << t << "\n";
}
// 8. replace()
{
std::string t = s;
t.replace(1, 3, "ABC");
std::cout << "replace: " << t << "\n";
}
// 9. copy()
{
std::string t = "hello world";
char buf[20] = {0};
std::size_t n = t.copy(buf, 5, 6); // 从下标 6 开始复制 5 个字符
buf[n] = '\0'; // copy 不会自动补 '\0'
std::cout << "copy: " << buf << "\n";
}
// 10. resize()
{
std::string t = s;
t.resize(8, 'x');
std::cout << "resize bigger: " << t << "\n";
t.resize(3);
std::cout << "resize smaller: " << t << "\n";
}
// 11. swap()
{
std::string a = "aaa";
std::string b = "bbb";
a.swap(b);
std::cout << "swap: a = " << a << ", b = " << b << "\n";
}
return 0;
}
查找
find从前往后查找给定子串是否出现,出现返回对应第一个元素的位置;rfind从后向前查找指定字符是否出现;find_first_of找到第一个是对应字符的的首字符位置,find_last_of找第一个不是的。选择合适的函数使用能事半功倍

- 代码示例
#include <iostream>
#include <string>
int main() {
std::string s = "hello world";
// 1. find(从前往后找子串)
std::size_t pos1 = s.find("world");
if (pos1 != std::string::npos)
std::cout << "find: " << pos1 << "\n";
// 2. rfind(从后往前找子串)
std::size_t pos2 = s.rfind("l");
if (pos2 != std::string::npos)
std::cout << "rfind: " << pos2 << "\n";
// 3. find_first_of(找第一个属于某集合的字符)
std::size_t pos3 = s.find_first_of("aeiou");
if (pos3 != std::string::npos)
std::cout << "find_first_of (vowel): " << pos3 << " -> " << s[pos3] << "\n";
// 4. find_first_not_of(找第一个不属于某集合的字符)
std::string t = " abc";
std::size_t pos4 = t.find_first_not_of(' ');
if (pos4 != std::string::npos)
std::cout << "find_first_not_of (skip space): " << pos4 << " -> " << t[pos4] << "\n";
// 5. find_last_of(找最后一个属于某集合的字符)
std::size_t pos5 = s.find_last_of("aeiou");
if (pos5 != std::string::npos)
std::cout << "find_last_of (vowel): " << pos5 << " -> " << s[pos5] << "\n";
// 6. find_last_not_of(找最后一个不属于某集合的字符)
std::string u = "abc ";
std::size_t pos6 = u.find_last_not_of(' ');
if (pos6 != std::string::npos)
std::cout << "find_last_not_of (trim right): " << pos6 << " -> " << u[pos6] << "\n";
return 0;
}
对字符串的操作

- 代码示例
#include <iostream>
#include <string>
int main() {
std::string s1 = "hello";
std::string s2 = "hello";
std::string s3 = "world";
// 1. compare()
std::cout << "compare(s1, s2): " << s1.compare(s2) << "\n"; // 0
std::cout << "compare(s1, s3): " << s1.compare(s3) << "\n"; // <0 或 >0
// 2. starts_with() (C++20)
#if __cplusplus >= 202002L
std::cout << "starts_with(\"he\"): " << std::boolalpha
<< s1.starts_with("he") << "\n";
#endif
// 3. ends_with() (C++20)
#if __cplusplus >= 202002L
std::cout << "ends_with(\"lo\"): " << std::boolalpha
<< s1.ends_with("lo") << "\n";
#endif
// 4. contains() (C++23)
#if __cplusplus >= 202302L
std::cout << "contains(\"ell\"): " << std::boolalpha
<< s1.contains("ell") << "\n";
#endif
// 5. substr()
std::string sub = s1.substr(1, 3); // 从 index=1 开始取 3 个字符
std::cout << "substr: " << sub << "\n";
return 0;
}
对string的介绍就到这里了,接下来我会写一篇关于string的模拟实现的文章,欢迎点赞关注,如果有写的不对的地方,还请批评指针。
更多推荐


所有评论(0)