C++----STL(string)
引言:STL简介
什么是STL
STL的六大组件
string类
概念
string是管理字符数组的顺序表。
标准库中的string类
sting类的文档介绍连接:cplusplus.com/reference/string/string/?kw=string
string类的常用接口说明
1.常见构造
注意:
#include<iostream>
using namespace std;
int main()
{
string s1;
string s2("张三");// string (const char* s);
string s3("hello world");
string s4(10, '*');// string (size_t n, char c);
string s5(s2);// string (const string& str);
cout << s1 << endl;
cout << s2 << endl;
cout << s4 << endl;
cout << s5 << endl;
// 使用s3的子串初始化s6,从索引6开始,长度为5的子串,即"world"
string s6(s3, 6, 5);// string (const string& str, size_t pos, size_t len = npos);
cout << s3 << endl;
cout << s6 << endl;
// 使用s3的子串初始化s7,从索引6开始到s3的末尾,即"world"
string s7(s3, 6);
cout << s7 << endl;
string s8(s3, 6, 100);// 尝试从s3的索引6开始获取长度为100的子串,但s3的长度不足,所以只取到s3的末尾,即"world"
cout << s8 << endl;
string url("https://en.cppreference.com/w/");
string sub1(url, 0, 5);
string sub2(url, 8, 15);
string sub3(url, 22);
cout << sub1 << endl;
cout << sub2 << endl;
cout << sub3 << endl;
return 0;
}
2.赋值操作
#include<iostream>
using namespace std;
int main()
{
string s1;
string s2("张三");
s1 = s2;
cout << s1 << endl;
return 0;
}
3.容量操作
- size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。
- clear()只是将string中有效字符清空,不改变底层空间大小。
- reserve(size_t res_arg=0):在C++的
string
使用中,调用reserve(100)
函数理论上应确保字符串对象至少预留足够的空间来存储100个字符(不包括终止的空字符\0
)。然而,实际预留的空间大小可能因编译器和库实现的不同而有所差异。在GCC等Linux环境下的编译器中,string
的实现可能会严格遵守reserve
的请求,调用后capacity()
返回的值接近或等于100。而在VS(特别是VS2013及更早版本)中,由于可能采用了“短字符串优化”技术,调用reserve(100)
后capacity()
返回的值可能会远大于100,以减少未来内存分配的次数。此外,不同编译器和库实现中的string
类对于内存管理的策略(如释放多余空间和扩大容量等)也可能有所不同。值得注意的是,为string
预留空间时,如果reserve
的参数小于字符串当前的底层空间总大小,那么reserve
不会改变其容量大小。 - resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
#include<iostream>
using namespace std;
void test_size_lenth_capacity()
{
string s1 = "Hello World!";
cout << s1.size() << endl;
cout << s1.length() << endl;
cout << s1.capacity() << endl;
if (!s1.empty()) {
s1.clear();
cout << s1.empty() << endl;
cout << s1.size() << endl;//清除后的有效字符个数会变
cout << s1.length() << endl;
cout << s1.capacity() << endl;//但是清除后的容量不会变
}
}
void testReserve()
{
string s;
s.reserve(100);
size_t sz = s.capacity();
cout << "capacity changed: " << sz << '\n';
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';
}
}
s.clear();
cout << "capacity changed: " << sz << '\n';
s.reserve(10);
sz = s.capacity();
cout << "capacity changed: " << sz << '\n';
}
void testResize()
{
string s1 = "Hello World!";
s1.reserve(50);
cout << "Reserved capacity of string: " << s1.capacity() << endl;
// resize: 改变字符串的大小,如果新大小大于当前大小,则填充字符(默认是空字符)
s1.resize(20, '*');
cout << "Resized string: " << s1 << endl;
}
void test_shrink_to_fit()
{
string s1 = "Hello World!";
cout << "Current capacity of string: " << s1.capacity() << endl;
s1.reserve(50);
cout << "Reserved capacity of string: " << s1.capacity() << endl;
s1.shrink_to_fit();
cout << "shrink the capacity of s1 to fit: " << s1.capacity() << endl;
}
4.访问及遍历操作
注意:operator[]有两种形式用于访问字符串中的字符
- 非const版本:用于非常量字符串,允许读取和修改字符。例如,str[i] = 'A'; 可以改变字符串str中索引为i的字符。
- const版本:用于常量字符串或当您不希望修改字符串时,它只允许读取字符而不允许修改。尝试修改const字符串中的字符会导致编译错误。
重点补充:迭代器
定义
迭代器(iterator)是一种类似指针的对象,用于访问容器中的元素。它可能是一个指针,也可能是一个封装了指针行为的对象。迭代器提供了对容器元素的顺序访问,可以遍历容器中的元素。
迭代器的使用
1.普通迭代器和反向迭代器的使用
普通迭代器允许读写容器中的元素,反向迭代器允许我们从后向前遍历容器。以下是一个使用迭代器遍历并修改 string
对象的例子:
#include <iostream>
#include <string>
using namespace std;
int main() {
string s1("hello world");
//普通迭代器
string::iterator it = s1.begin();
while (it != s1.end()) {
(*it)--; // 将每个字符的ASCII值减1
++it;
}
cout << s1 << endl; // 输出修改后的字符串
//反向迭代器
auto rit = s1.rbegin();// 使用auto关键字简化类型声明
while (rit != s1.rend())
{
(*rit) ++;// 将每个字符的ASCII值加1
++rit;
}
cout << s1 << endl;
return 0;
}
2.const迭代器的使用
对于 const
对象或需要保证不修改容器内容的场景,应使用 const
迭代器。const
迭代器只能读取元素,不能修改。
#include <iostream>
#include <string>
using namespace std;
void Func(const std::string& s) {
string::const_iterator it = s.begin();
while (it != s.end()) {
cout << *it << " ";
++it;
}
cout << endl;
auto rit = s.rbegin(); // 使用auto关键字简化类型声明
while (rit != s.rend()) {
cout << *rit << " ";
++rit;
}
cout << endl;
}
int main() {
string s1("hello world");
Func(s1);
return 0;
}
迭代器的意义
-
修改元素:迭代器可以像指针一样解引用并修改元素(在非
const
迭代器的情况下)。 -
范围for循环:C++11引入了范围for循环,底层通过迭代器实现,简化了遍历容器的代码。
#include <iostream> #include <string> using namespace std; int main() { string s1("hello world"); for (auto& ch : s1) { ch++; // 将每个字符的ASCII值加1 } cout << s1 << endl; for (char ch : s1) { cout << ch << " "; } cout << endl; return 0; }
-
通用性:几乎所有标准容器(如
vector
、list
、map
等)都支持迭代器,并且用法类似。这使得代码更加通用和可移植。#include <iostream> #include <vector> #include <list> using namespace std; int main() { vector<int> v = {1, 2, 3, 4}; vector<int>::iterator vit = v.begin(); while (vit != v.end()) { cout << *vit << " "; ++vit; } cout << endl; list<int> lt = {10, 20, 3, 4}; list<int>::iterator lit = lt.begin(); while (lit != lt.end()) { cout << *lit << " "; ++lit; } cout << endl; return 0; }
-
限制:对于非连续存储的容器(如
list
、map
),不能使用下标操作符[]
访问元素,因为它们的空间结构不是连续的。 -
与算法配合:STL算法(如
reverse
、sort
等)通过迭代器操作容器中的元素,使得算法与容器解耦,提高了代码的灵活性和复用性。#include <iostream> #include <vector> #include <list> #include <algorithm> using namespace std; int main() { vector<int> v = {1, 2, 3, 4}; list<int> lt = {10, 20, 3, 4}; reverse(v.begin(), v.end()); reverse(lt.begin(), lt.end()); for (auto e : v) { cout << e << " "; } cout << endl; for (auto e : lt) { cout << e << " "; } cout << endl; sort(v.begin(), v.end()); for (auto e : v) { cout << e << " "; } cout << endl; return 0; }
补充:at的代码示例
void testAt()
{
string s1 = "Hello World!";
cout << s1.at(2) << endl;
s1.at(2) = 'O';
cout << s1 << endl;
}
5.修改操作
函数名称 | 功能说明 |
push_back | 在字符串末尾添加一个字符 |
append | 在字符串末尾追加一个字符串 |
operator+= | 使用加法运算符追加字符串或字符 |
assign | 覆盖赋值,用新字符串替换原字符串内容 |
insert | 在指定位置插入字符串或字符(不建议频繁使用,会影响效率) |
erase | 删除指定位置或范围内的字符(不建议频繁使用,会影响效率) |
pop_back | 删除字符串末尾的字符 |
replace | 替换指定范围内的字符为新的字符串或字符 |
void test01()
{
string s1("hello world");
s1.append("ssssss"); // s1 变为 "hello worldssssss"
s1.push_back('a'); // s1 变为 "hello worldssssssa"
s1+=" abc"; // s1 变为 "hello worldssssssa abc"
s1.assign("111111111"); // s1 变为 "111111111"
s1.insert(0, "hello"); // s1 变为 "hello111111111"
s1.insert(5, "world"); // s1 变为 "helloworld11111"
s1.insert(0, 10, 'x'); // s1 变为 "xxxxxxxxxxhelloworld111"
s1.insert(s1.begin()+10, 10, 'y'); // s1 变为 "xxxxxxxxxxhyyyyyyyworld11"
}
void test02()
{
string s1("hello world");
s1.erase(5, 1); // 删除从位置5开始的1个字符,s1 变为 "hello world" -> "helloworld"
s1.erase(5); // 删除从位置5开始的剩余所有字符(默认行为),s1 变为 "hello"
string s2("hello world");
s2.erase(0, 1); // 删除从位置0开始的1个字符,s2 变为 "hello world" -> "ello world"
s2.erase(s2.begin()); // 删除迭代器指向的第一个字符,s2 变为 "llo world"
}
void test03()
{
//world替换成 xxxxxxxxxxxxxxxxxxxxxx
string s1("hello world hello lynn");
s1.replace(6, 5, "xxxxxxxxxxxxxxxxxxxxxx"); // 替换 "world" 为 "xxxxxxxxxxxxxxxxxxxxxx"
cout << s1 << endl; // 再次替换,s1 变为 "hello yyyyy"
s1.replace(6, 23, "yyyyy");
cout << s1 << endl;
// 所有空格替换成20%
string s2("hello world hello bit");
string s3;
for (auto ch : s2)
{
if (ch != ' ')
{
s3 += ch;
}
else
{
s3 += "20%";
}
}
s2 = s3; // s2 变为 "hello20%world20%hello20%lynn"
}
6.字符串操作
函数名称 | 功能说明 |
c_str | 返回一个指向以空字符结尾的字符数组的指针,该字符数组是字符串的副本,常用于与C语言接口函数的交互。 |
find | 用于查找子字符串或字符在字符串中第一次出现的位置。 |
rfind | 用于查找子字符串或字符在字符串中最后一次出现的位置。 |
find_first_of | 用于查找字符串中第一次出现参数中任一字符的位置。 |
find_last_of | 用于查找字符串中最后一次出现参数中任一字符的位置。 |
substr | 用于查找字符串中最后一次出现参数中任一字符的位置。 |
void test_c_str() {
string str = "Hello, World!";
const char* cStr = str.c_str();
cout << cStr << endl; // 输出: Hello, World!
}
void test_find() {
string str = "Hello, World!";
size_t pos = str.find("World");
if (pos != string::npos) {
cout << "Found 'World' at position: " << pos << endl; // 输出: Found 'World' at position: 7
}
else {
cout << "'World' not found" << endl;
}
}
void test_rfind() {
string str = "Hello, Hello World!";
size_t pos = str.rfind("Hello");
if (pos != string::npos) {
cout << "Found 'Hello' last time at position: " << pos << endl; // 输出: Found 'Hello' last time at position: 13
}
else {
cout << "'Hello' not found" << endl;
}
}
void test_find_first_of() {
string str = "Hello, World!";
size_t pos = str.find_first_of("aeiou");
if (pos != string::npos) {
cout << "Found first vowel at position: " << pos << endl; // 输出: Found first vowel at position: 1 (e in Hello)
}
else {
cout << "No vowel found" << endl;
}
}
void test_find_last_of() {
string str = "Hello, World!";
size_t pos = str.find_last_of("aeiou");
if (pos != string::npos) {
cout << "Found last vowel at position: " << pos << endl; // 输出: Found last vowel at position: 8 (o in World)
}
else {
cout << "No vowel found" << endl;
}
}
void test_substr() {
string str = "Hello, World!";
string subStr1 = str.substr(7, 5); // 从位置7开始,长度为5的子字符串
cout << "Substring1: " << subStr1 << endl; // 输出: Substring1: World
string subStr2 = str.substr(2, 6); // 从位置2开始,长度为6的子字符串
cout << "Substring2: " << subStr2 << endl; // 输出: Substring2: llo, W
}
7.string类非成员函数
函数名称 | 功能说明 |
operator<< | 重载输出流运算符<<用于将自定义类型的数据输出到输出流。 |
operator>> | 重载输入流运算符>>用于从输入流中读取数据到自定义类型。 |
getline | getline 是标准库函数,用于从输入流中读取一行文本,直到遇到换行符('\n'),换行符会被丢弃,不会存储在字符串中。 |
operator+ | 重载operator+用于连接两个字符串。 |
operator==、operator>、 operator<、 operator<=、 operator>=、 operator!= | 重载关系运算符(如==, <, >, <=, >=, !=)用于比较自定义类型的对象。 当使用关系运算符比较两个字符串时,比较是逐字符进行的,基于Unicode码点值。 |
#include<algorithm>
using namespace std;
int main() {
string s1, s2;
cin >> s1;//如果输入空格,就会停止输入
cout << s1 << endl;
//注意:如果 cin >> s1; 读取后留下了换行符,getline() 会立即读取到这个换行符并停
// 止读取,认为已经读取了一行(实际上是一个空行)。因此,s2 会是一个空字符串。
// 为了避免这种问题,我们要通过cin.ignore(); 忽略缓冲区中的换行符。
cin.ignore();// 忽略缓冲区中的下一个字符(通常是换行符)
getline(cin, s2);//如果输入空格,不会停止输入,直到遇到换行符'\n',才会停止输入
cout << s2 << endl;
string s3 = s1 + s2;
cout << s3 << endl;
string s4, s5;
s4 = "abcd";
s5 = "abc";
if (s4 > s5) {
cout << "s4 > s5" << endl;
}
else {
cout << "s4 <= s5" << endl;
}
return 0;
}
8.字符串转换函数
函数名称 | 功能说明 |
| 将字符串转换为整数( |
| 将字符串转换为单精度浮点数( |
| 将字符串转换为双精度浮点数( |
| 将字符串转换为长长整数( |
| 将数值转换为字符串 |
int main() {
string str1 = "123";
int num1 = stoi(str1);
cout << "The int value is: " << num1 << endl;
string str2 = "123.456";
float num2 = stof(str2);
cout << "The float value is: " << num2 << endl;
string str3 = "123";
double num3 = stod(str3);
cout << "The double value is: " << num3 << endl;
string str4 = "12345678910111213";
long long num4 = stoll(str4);
cout << "The long long value is: " << num4 << endl;
int num5 = 123;
string str5 = to_string(num5);
cout << "The string value is: " << str5 << endl;
long long num6 = 1234567;
string str6 = to_string(num6);
cout << "The string value is: " << str6 << endl;
double num7 = 3.14;
string str7 = to_string(num7);
cout << "The string value is: " << str7 << endl;
return 0;
}
写时拷贝(了解)
一、浅拷贝问题
- 析构两次:浅拷贝只复制了对象的指针,导致两个对象共享同一块内存。当这两个对象被分别析构时,会尝试对同一块内存进行两次释放,从而引发错误。
- 一个对象修改会影响另一个:由于浅拷贝只是复制了指针,所以两个对象实际上指向的是同一块数据。因此,当一个对象的数据被修改时,另一个对象的数据也会相应地被改变。
二、深拷贝问题
- 内存开销:深拷贝需要复制对象的所有数据,包括嵌套对象,这可能导致较大的内存开销。
- 写时拷贝优化:为解决深拷贝的内存开销问题,可采用写时拷贝策略。
初始时,对象间共享数据内存,引用计数跟踪使用对象数量。
当某个对象尝试修改数据时,若引用计数大于1,则进行深拷贝,生成新的数据副本,以避免影响其他对象。 - 引用计数管理:
(注意:引用计数需正确管理,以确保内存安全释放。)
当对象被复制时,引用计数增加;当对象被销毁或不再被引用时,引用计数减少。
引用计数降为0时,释放对象及其数据内存。
原文地址:https://blog.csdn.net/2403_82706967/article/details/144799086
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!