C++ string 类(标准库中的string类)

发布于:2025-07-03 ⋅ 阅读:(14) ⋅ 点赞:(0)

       //     欢迎来到 aramae 的博客,愿 Bug 远离,好运常伴!  //

时代不会辜负长期主义者,愿每一个努力的人都能达到理想的彼岸。
    引言: 我们都知道STL是C++的重要部分,STL(Standard Template Library)与 C++ 的string类密切相关,它们共同构成了 C++ 标准库的核心部分,因此学习string类时会将两者紧密联系起来学习。下面学习string时会和参考 C++ 标准文档(如cppreference.com)和 SGI STL 源码。(下篇讲string的模拟实现)

    注意:学习STL不要想着掌握所有函数,只要掌握最常见的就可以了(大概二十几个),剩下的不常用的,有个印象,需要时查文档就行。学习这块最好的方式是阅读文档加自己动手实现一遍。

    https://cppreference.com   --> C++官网 (较繁琐,查找不便,感兴趣可以看看)

    string - C++ Reference       -->(盗版网站,查找方便,建议阅读)

    注:初步学习STL,不建议阅读 stl源码剖析,等进阶时阅读效果更好


     下面就 文档 + 代码 做简单学习理解,最后做一些练习题目

    1. 什么是string类?

    std::string 是 C++ 标准库提供的字符串类(位于 <string> 头文件),用于安全、高效地处理可变长度的字符序列。

    string类的文档介绍:

    1. 字符串是表示字符序列的类
    2.  标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。
    3. string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信息,请参阅basic_string)。
    4.  string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits 和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。
    5. 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。
    总结:
    • 1. string是表示字符串的字符串类
    • 2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。3. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator> string;
    • 4. 不能操作多字节或者变长字符的序列。
    使用string类时,必须包含#include头文件以及using namespace std;

    2. string类的常用接口说明  

    2.1 string类对象的成员函数  

    2.11 常见构造函数

    构造字符串对象

    构造一个字符串对象,根据使用的构造函数版本初始化其值:

      (1) std :: string();

     空字符串构造函数(默认构造函数)构造一个长度为零个字符的空字符串

      (2)std::string (const string& str);

      复制构造函数  构造 str 的副本。

     (3)std::string (const string& str, size_t pos, size_t len = npos);(了解)

    子字符串构造函数    复制从字符位置 pos 开始并跨越 len 字符的 str 部分(或者直到 str 的末尾,如果 str 太短或 len 为 string::npos)。

    (4)std::string (const char* s);

    从 C 字符串复制由 s 指向的以 null 结尾的字符序列(C 字符串)。

    (5)std::string (const char* s, size_t n);(了解)

    来自缓冲区   从 s 指向的字符数组中复制前 n 个字符。

    (6)std::string (size_t n, char c);

    填充构造函数   用字符 c 的 n 个连续副本填充字符串。

    示例:

    #include <iostream>
    #include <string>
    #include <vector>
    
    int main() {
        // (1) 默认构造函数
        std::string s1;
        std::cout << "s1: " << s1 << std::endl;  // 输出: s1: 
    
        // (2) 复制构造函数
        std::string s2("hello");
        std::string s3(s2);
        std::cout << "s3: " << s3 << std::endl;  // 输出: s3: hello
    
        // (3) 子字符串构造函数
        std::string s4(s2, 1, 3);  // 从位置1开始的3个字符
        std::cout << "s4: " << s4 << std::endl;  // 输出: s4: ell
    
        // (4) 从C字符串构造
        const char* cstr = "world";
        std::string s5(cstr);
        std::cout << "s5: " << s5 << std::endl;  // 输出: s5: world
    
        // (5) 从字符序列构造(前n个字符)
        std::string s6(cstr, 3);  // 取前3个字符
        std::cout << "s6: " << s6 << std::endl;  // 输出: s6: wor
    
        // (6) 填充构造函数
        std::string s7(5, '!');  // 5个连续的'!'
        std::cout << "s7: " << s7 << std::endl;  // 输出: s7: !!!!!
    
        // (7) 范围构造函数(从迭代器范围)
        std::vector<char> chars = {'H', 'e', 'l', 'l', 'o'};
        std::string s8(chars.begin(), chars.end());
        std::cout << "s8: " << s8 << std::endl;  // 输出: s8: Hello
    
        return 0;
    }
    #include<iostream>
    using namespace std;
    
    int main()
    {
    	string s1();//构造空的string对象
    	string s2("zhang san");//用C格式字符串构造string类对象s2
    	string s3(s2); // 拷贝构造s3
    }
    2.12 析构函数(了解)

    2.13  std::string::operator=(了解)


    2.2 string类的迭代器 (iterator) (补充)

    • 迭代器是 C++ 为简化容器访问而设计的抽象工具,提供安全、统一的遍历接口。
    • 指针是 C++ 从 C 继承的底层机制,提供直接操作内存的能力,但需手动管理内存安全。

    2.21. 迭代器类型

    string类有以下几种迭代器类型:

    1. 正向迭代器 (iterator)

      可读写,用于正向遍历容器(从前往后)。
    2. 常量正向迭代器 (const_iterator)

      只读,用于遍历常量容器或禁止修改元素的场景。
    3. 反向迭代器 (reverse_iterator)

      可读写,用于反向遍历容器(从后往前)。
    4. 常量反向迭代器 (const_reverse_iterator)

      只读,用于反向遍历常量容器
    string::iterator          // 用于正向遍历的可变迭代器
    string::const_iterator    // 用于正向遍历的常量迭代器
    string::reverse_iterator  // 用于反向遍历的可变迭代器
    string::const_reverse_iterator // 用于反向遍历的常量迭代器
    

    2.22. 迭代器的初始化

    迭代器可借助begin()end()rbegin()rend()函数来初始化:

    #include <string>
    #include <iostream>
    
    int main() {
        std::string str = "Hello";
        
        // 正向迭代器
        std::string::iterator it = str.begin();     // 指向第一个字符'H'
        std::string::iterator end = str.end();      // 指向最后一个字符的下一个位置
        
        // 反向迭代器
        std::string::reverse_iterator rit = str.rbegin(); // 指向最后一个字符'o'
        std::string::reverse_iterator rend = str.rend();  // 指向第一个字符的前一个位置
        
        return 0;
    }
    

    2.23. 利用迭代器遍历字符串

    正向遍历

    std::string str = "World";
    for (std::string::iterator it = str.begin(); it != str.end(); ++it) {
        std::cout << *it;  // 输出: W o r l d
    }
    

    也能使用常量迭代器来遍历常量字符串

    const std::string cstr = "Hello";
    for (std::string::const_iterator cit = cstr.begin(); cit != cstr.end(); ++cit) {
        std::cout << *cit;
    }
    

    反向遍历

    std::string str = "Hello";
    for (std::string::reverse_iterator rit = str.rbegin(); rit != str.rend(); ++rit) {
        std::cout << *rit;  // 输出: o l l e H
    }
    

    2.24. 借助迭代器修改字符串

    可变迭代器能够修改字符串中的字符:

    std::string str = "hello";
    for (std::string::iterator it = str.begin(); it != str.end(); ++it) {
        *it = std::toupper(*it);  // 将字符转换为大写
    }
    std::cout << str;  // 输出: HELLO
    

    2.25. C++11 范围 for 循环

    C++11 引入的范围 for 循环让代码更为简洁。在底层,它也是通过迭代器来实现的:

    std::string str = "Hello";
    for (char c : str) {  // 对于只读操作,使用const char&更佳
        std::cout << c;
    }
    
    // 修改字符
    for (char& c : str) {  // 必须使用引用才能修改原字符串
        c = std::tolower(c);
    }
    

    2.26. 迭代器的运算

    string类的迭代器支持多种运算符

    std::string str = "0123456789";
    std::string::iterator it = str.begin();
    
    // 访问第5个字符
    it += 4;  // 移动4步,指向索引4的字符
    std::cout << *it;  // 输出: 4
    
    // 比较迭代器
    if (it < str.end()) {
        std::cout << "在有效范围内";
    }
    

    2.27. 迭代器失效问题

    对字符串进行插入或删除操作可能会使迭代器失效,这意味着不能再使用失效的迭代器:

    std::string str = "abc";
    std::string::iterator it = str.begin();
    
    str.insert(it, 'x');  // 在开头插入'x'
    // 此时原迭代器it已失效,不能再使用
    it = str.begin();  // 重新获取有效迭代器

    例: 

    #include <iostream>
    #include <vector>
    
    int main() {
        std::vector<int> vec = {1, 2, 3, 4, 5};
    
        // 1. 正向遍历(可修改)
        std::cout << "正向遍历:";
        for (auto it = vec.begin(); it != vec.end(); ++it) {
            *it *= 2;  // 可修改元素
            std::cout << *it << " ";  // 输出: 2 4 6 8 10
        }
        std::cout << std::endl;
    
        // 2. 反向遍历(可修改)
        std::cout << "反向遍历:";
        for (auto rit = vec.rbegin(); rit != vec.rend(); ++rit) {
            *rit += 1;  // 可修改元素
            std::cout << *rit << " ";  // 输出: 11 9 7 5 3
        }
        std::cout << std::endl;
    
        // 3. 常量正向遍历(不可修改)
        const std::vector<int> cvec = {10, 20, 30};
        std::cout << "常量正向遍历:";
        for (auto cit = cvec.cbegin(); cit != cvec.cend(); ++cit) {
            // *cit = 100;  // 错误:不能修改常量迭代器指向的元素
            std::cout << *cit << " ";  // 输出: 10 20 30
        }
        std::cout << std::endl;
    
        // 4. 常量反向遍历(不可修改)
        std::cout << "常量反向遍历:";
        for (auto crit = cvec.crbegin(); crit != cvec.crend(); ++crit) {
            std::cout << *crit << " ";  // 输出: 30 20 10
        }
        std::cout << std::endl;
    
        return 0;
    }

    2.3 string类对象的访问及遍历操作 

    在 C++ 中,遍历字符串或容器除了使用迭代器,还有其他多种写法,具体取决于你的需求和代码风格。以下是几种常见的替代方法:

    2.31. 基于范围的 for 循环(Range-based for loop)

    最简洁的写法,适用于 C++11 及以后的版本:

    #include <iostream>
    #include <string>
    
    int main() {
        std::string s1 = "Hello";
        
        // 自动遍历每个字符,c是字符的拷贝
        for (char c : s1) {
            std::cout << c << std::endl;
        }
        
        // 如果需要修改字符,使用引用(&)
        for (char& c : s1) {
            c = std::toupper(c); // 将字符转为大写
        }
        
        return 0;
    }
    
    2.32. 传统的下标遍历

    通过索引访问字符,适用于所有 C++ 版本:

    #include <iostream>
    #include <string>
    
    int main() {
        std::string s1 = "Hello";
        
        // 使用size_t避免整数溢出,i < s1.size()确保不会越界
        for (size_t i = 0; i < s1.size(); ++i) {
            std::cout << s1[i] << std::endl;
        }
        
        return 0;
    }
    
    2.33. 反向迭代器(Reverse Iterator)

    从后向前遍历,使用rbegin()rend()

    #include <iostream>
    #include <string>
    
    int main() {
        std::string s1 = "Hello";
        
        // 反向迭代器,输出 "olleH"
        for (auto it = s1.rbegin(); it != s1.rend(); ++it) {
            std::cout << *it << std::endl;
        }
        
        return 0;
    }
    
    2.34. 常量迭代器(Const Iterator)

    用于只读访问,防止修改字符串内容:

    #include <iostream>
    #include <string>
    
    int main() {
        const std::string s1 = "Hello";
        
        // 使用const_iterator,禁止修改字符
        for (std::string::const_iterator it = s1.begin(); it != s1.end(); ++it) {
            std::cout << *it << std::endl;
            // *it = 'X';  // 错误:不能修改常量迭代器指向的内容
        }
        
        // 或者使用auto自动推导类型
        for (auto it = s1.cbegin(); it != s1.cend(); ++it) {
            std::cout << *it << std::endl;
        }
        
        return 0;
    }
    
    2.35. 使用 STL 算法(如std::for_each

    结合 lambda 表达式,适用于更复杂的操作:

    #include <iostream>
    #include <string>
    #include <algorithm>
    
    int main() {
        std::string s1 = "Hello";
        
        // 对每个字符执行lambda函数
        std::for_each(s1.begin(), s1.end(), [](char c) {
            std::cout << c << std::endl;
        });
        
        return 0;
    }
    
    2.36. 手动管理迭代器(不推荐,易出错)

    直接操作迭代器指针,需要手动控制迭代过程:

    #include <iostream>
    #include <string>
    
    int main() {
        std::string s1 = "Hello";
        auto it = s1.begin();
        
        while (it != s1.end()) {
            std::cout << *it << std::endl;
            it += 2; // 跳步遍历(输出'H', 'l', 'o')
        }
        
        return 0;
    }
    

    选择建议:
    • 优先使用基于范围的 for 循环:代码最简洁,不易出错。
    • 需要索引时使用下标遍历:例如需要同时访问相邻元素。
    • 反向遍历时使用反向迭代器:避免手动计算索引。
    • 只读访问使用常量迭代器:提高代码安全性。
    • 复杂操作使用 STL 算法:例如查找、转换等。
    汇总:                                                                                                                                                
    #include<iostream>
    #include<string>
    #include <algorithm>
    using namespace std;
    
    int main()
    {
    	string s1("hello aramae");
    
    	//正向迭代器
    	//方法一:
    	string::iterator it = s1.begin();
    	while (it != s1.end())
    	{
    		cout << *it << " ";
    		++it;
    	}
    	cout << endl;
    
    	//方法二:
    	for (auto i = s1.begin(); i < s1.end(); ++i)
    	{
    		cout << *i << " ";
    	}
    	cout << endl;
    
    
    	/*反向迭代器(Reverse Iterator)
    	从后向前遍历,使用rbegin()和rend():*/
    	//方法一:
    	string::reverse_iterator rit = s1.rbegin();
    	while (rit != s1.rend())
    	{
    		cout << *rit << " ";
    		++rit;
    	}
    	cout << endl;
    
    	//方法二:
    	for (auto rit = s1.rbegin(); rit < s1.rend(); ++rit)
    	{
    		cout << *rit << " ";
    	}
    	cout << endl;
    
    	return 0;
    
    
    	//基于范围的 for 循环(Range-based for loop)
    	//最简洁的写法,适用于 C++11 及以后的版本:
    	/*for (auto&a1 : s1)
    	{
    		cout << a1+1 << " ";
    	}
    	cout << endl*/;
    
    	for (char a1 : s1)
    	{
    		cout << a1 << " ";
    	}
    	cout << endl;
    
    	//如果需要改变s1
    	for (char& a2 : s1)
    	{
    		cout << a2 + 1 << " ";
    	}
    	cout << endl;
    
    
    	/*传统的下标遍历
    	通过索引访问字符,适用于所有 C++ 版本:*/
    	for (size_t i = 0; i < s1.size(); ++i)
    	{
    		cout << s1[i] << " ";
    	}
    	cout << endl;
    
    
    	//常量迭代器:
    	// 使用const_iterator,禁止修改字符
    	for (string::const_iterator it = s1.begin(); it != s1.end(); ++it) {
    		cout << *it << endl;
    		// *it = 'X';  // 错误:不能修改常量迭代器指向的内容
    	}
    	// 或者使用auto自动推导类型
    	for (auto it = s1.cbegin(); it != s1.cend(); ++it) {
    		std::cout << *it << std::endl;
    	}
    
    	/*使用 STL 算法(如std::for_each)
    	结合 lambda 表达式,适用于更复杂的操作*/
    	// 对每个字符执行lambda函数
    	 std::for_each(s1.begin(), s1.end(), [](char c) {
    		std::cout << c << std::endl;
    		});
    
    
         //最推荐:
    	 for (auto& c : s1)
    	 {
    		 cout << c << " ";
    	 }
    }

    2.4 string类对象的容量操作

    (1)std::string::size 

       返回字符串的当前长度(即字符数量)

    #include <iostream>
    #include <string>
    
    int main() {
        std::string s = "hello";
        std::cout << "Size: " << s.size();  // 输出: 5
    }

     (2) std::string::length 

      与 size() 完全等价,返回字符串的当前长度

    #include <iostream>
    #include <string>
    
    int main() {
        std::string s = "world";
        std::cout << "Length: " << s.length();  // 输出: 5
    }

    (3)std::string::resize

       整字符串长度,可能截断或填充新字符

    #include <iostream>
    #include <string>
    
    int main() {
        std::string s = "hello";
        
        // 截断字符串
        s.resize(3);
        std::cout << s << "\n";  // 输出: hel
        
        // 扩展字符串,用 'x' 填充
        s.resize(5, 'x');
        std::cout << s << "\n";  // 输出: helxx
        
        // 扩展字符串,默认用 '\0' 填充
        s.resize(7);
        std::cout << "Size: " << s.size();  // 输出: 7
        std::cout << "Content: " << s;      // 输出: helxx(后面的 '\0' 不可见)
    }

    (4)std::string::capacity 

    返回当前分配的内存可容纳的字符数(不重新分配内存的情况下)

    #include <iostream>
    #include <string>
    
    int main() {
        std::string s = "hello";
        std::cout << "Size: " << s.size();       // 输出: 5
        std::cout << "Capacity: " << s.capacity(); // 输出: 至少5(具体值取决于实现)
        
        // 添加字符可能触发内存重新分配
        s += " world";
        std::cout << "New Capacity: " << s.capacity(); // 输出: 通常大于11(动态扩展)
    }

    (5)std::string::reserve 

    避免频繁重新分配

    #include <iostream>
    #include <string>
    
    int main() {
        std::string s;
        s.reserve(100);  // 预先分配100个字符的空间
        
        std::cout << "Size: " << s.size();       // 输出: 0
        std::cout << "Capacity: " << s.capacity(); // 输出: 至少100
        
        s = "hello";
        std::cout << "New Capacity: " << s.capacity(); // 输出: 至少100(保持不变)
    }

    ( 6)std::string::clear

    清空字符串内容,但不释放内存(容量不变)

    #include <iostream>
    #include <string>
    
    int main() {
        std::string s = "hello";
        s.reserve(100);
        
        std::cout << "Before clear - Size: " << s.size();       // 输出: 5
        std::cout << "Before clear - Capacity: " << s.capacity(); // 输出: 100
        
        s.clear();
        
        std::cout << "After clear - Size: " << s.size();       // 输出: 0
        std::cout << "After clear - Capacity: " << s.capacity(); // 输出: 100(容量不变)
    }

    (7)std::string::empty 

    检查字符串是否为空(即长度是否为 0)

    #include <iostream>
    #include <string>
    
    int main() {
        std::string s1 = "";
        std::string s2 = "hello";
        
        std::cout << std::boolalpha; // 输出 true/false 而非 1/0
        std::cout << "s1 empty: " << s1.empty(); // 输出: true
        std::cout << "s2 empty: " << s2.empty(); // 输出: false
    }
    int main()
    {
    	string s0("hello aramae!");
    
    	cout << "size:" << s0.size() << endl;
    	
    	cout << "length:" << s0.length() << endl;
    
    	s0.resize(5);
    	cout << s0 << endl;
    
    	s0.resize(13, 'x');
    	cout << s0 << endl;
    
    	s0.resize(20);
    	cout << s0.size() << endl;
    	cout << s0 << endl;
    
    	cout << "size:" << s0.size() << endl;
    	cout << "capacity:" << s0.capacity() << endl;
    
    	string s1;
    	s1.reserve(100);
    
    	cout << "size:" << s1.size() << endl;        //size: 0
    	cout << "capacity:" << s1.capacity() << endl;//capacity : 111
    
    	string s2="hello";
    	cout << "new capacity:" << s1.capacity() << endl;//new capacity: 111
    	
    	s2.clear();
    	cout << "size:" << s1.size() << endl;
    	cout << "capzcity:" << s1.capacity() << endl; //capacity : 111
    
    	string s3 = "";
    	string s4 = "hello";
    
    	cout << std::boolalpha; // 输出 true/false 而非 1/0
    	cout << "s3 empty: " << s3.empty()<<endl; // 输出: true
    	cout << "s4 empty: " << s4.empty()<<endl; // 输出: false
    
    	return 0;
    }
      注意:
    • 1. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一 致,一般情况下基本都是用size()。
    • 2. clear()只是将string中有效字符清空,不改变底层空间大小。
    • 3. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字 符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的 元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
    • 4. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于 string的底层空间总大小时,reserver不会改变容量大小。

    2.5 string类对象的修改操作

    (这里只讲部分常用的,剩下的需要时查文档即可)

    (1)std::string::operator+=

    这个函数用于在字符串末尾添加字符或者字符串。

    #include <iostream>
    #include <string>
    
    int main() {
        std::string str = "Hello";
        str += " World";     // 添加字符串
        str += '!';         // 添加单个字符
        std::cout << str;   // 输出:Hello World!
        return 0;
    }
    (2)std::string::append

    该函数能够把字符串、字符串的子串或者字符数组添加到当前字符串的末尾

    #include <iostream>
    #include <string>
    
    int main() {
        std::string str = "Hello";
        str.append(" World");            // 添加字符串
        str.append("!!!", 2);           // 添加"!!!"的前2个字符
        std::cout << str;               // 输出:Hello World!!
        return 0;
    }
    (3)std::string::push_back

    此函数的作用是在字符串末尾添加一个字符,和operator +=添加单个字符的功能类似

    #include <iostream>
    #include <string>
    
    int main() {
        std::string str = "ABC";
        str.push_back('D');
        std::cout << str;   // 输出:ABCD
        return 0;
    }
    (4)std::string::c_str

    该函数会返回一个指向以\0结尾的 C 风格字符串的指针,其内容和当前std::string相同,主要用于和 C 语言的 API 进行交互

    #include <iostream>
    #include <string>
    #include <cstdio>
    
    int main() {
        std::string str = "Hello";
        const char* cstr = str.c_str();
        std::printf("C-style string: %s\n", cstr);
        return 0;
    }
    (5)std::string::find

    这个函数用于在字符串中查找子串或者字符首次出现的位置,如果找到了就返回其索引,没找到则返回std::string::npos

    #include <iostream>
    #include <string>
    
    int main() {
        std::string str = "Hello World";
        size_t pos = str.find("World");
        if (pos != std::string::npos) {
            std::cout << "Found at position: " << pos;  // 输出:Found at position: 6
        }
        return 0;
    }
    (6)std::string::rfind

    rfindfind类似,不过它是从字符串末尾开始向前查找子串或者字符首次出现的位置(也就是从后往前找第一个出现的位置)

    #include <iostream>
    #include <string>
    
    int main() {
        std::string str = "Hello World";
        size_t pos = str.rfind("l");
        if (pos != std::string::npos) {
            std::cout << "Last 'l' at position: " << pos;  // 输出:Last 'l' at position: 9
        }
        return 0;
    }
    (7)std::string::substr

    该函数用于从当前字符串中提取子串,它有两个参数,第一个参数是起始位置,第二个参数是子串的长度(如果省略第二个参数,则会提取从起始位置到字符串末尾的所有字符

    #include <iostream>
    #include <string>
    
    int main() {
        std::string str = "Hello World";
        std::string sub = str.substr(6, 5);  // 从位置6开始,提取5个字符
        std::cout << sub;                    // 输出:World
        return 0;
    }

    使用这些函数时要留意边界条件,比如find和rfind在未找到目标时会返回std::string::npos,substr的起始位置不能超出字符串的长度等 

    汇总:                                                                                                                                              

    int main()
    {
    	string str("hello ");
    	str += "aramae";
    	str += "!";
    	cout << str << endl;
    
    	string str1 = "hi";
    	str1.append(" aramae");
    	cout << str1 << endl;
    	str1.append("!!!!", 3);
    	cout << str1 << endl;
    
    	string str2("ABC");
    	str2.push_back('D');
    	cout << str2 << endl;
    
    	string str3("Hello world");
    	size_t pos = str.find("world");
    	if (pos != string::npos) {
    		std::cout << "Found at position: " << pos;  // 输出:Found at position: 6
    	}
    
    	string sub = str3.substr(6, 5);  // 从位置6开始,提取5个字符
    	cout << sub;
    
    	return 0;
    }
    注意:
    1. 在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差不多,一般
    情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。
    2. 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。

    2.6 string类非成员函数

    (1)std::string::operator+

    这个运算符的作用是连接两个字符串,或者把字符串和字符连接起来,连接完成后会返回一个新的字符串对象

    #include <iostream>
    #include <string>
    
    int main() {
        std::string str1 = "Hello";
        std::string str2 = " World";
        std::string result = str1 + str2;  // 连接两个字符串
        result = result + '!';             // 连接字符串和字符
        std::cout << result;               // 输出:Hello World!
        return 0;
    }
    (2)std::string::operator>>

    该运算符是输入流运算符,它会从输入流(像std::cin)里读取内容,直到遇到空白字符(例如空格、制表符、换行符等)为止,然后把读取到的内容存储到字符串中

    #include <iostream>
    #include <string>
    
    int main() {
        std::string name;
        std::cout << "Enter your name: ";
        std::cin >> name;  // 输入"John Doe",只会读取"John"
        std::cout << "Hello, " << name;  // 输出:Hello, John
        return 0;
    }
    (3)std::string::operator<<

    此运算符是输出流运算符,它的功能是把字符串的内容输出到输出流(例如std::cout)中

    #include <iostream>
    #include <string>
    
    int main() {
        std::string message = "Hello, C++!";
        std::cout << message;  // 输出:Hello, C++!
        return 0;
    }
    (4)std::string::getline

    这是一个独立的函数(并非std::string的成员函数),它能够从输入流中读取一整行内容,直到遇到换行符为止,并且会把读取到的内容存储到字符串中

    #include <iostream>
    #include <string>
    
    int main() {
        std::string address;
        std::cout << "Enter your address: ";
        std::getline(std::cin, address);  // 可以读取包含空格的整行内容
        std::cout << "Your address: " << address;
        return 0;
    }
    (5) std::string::relational operators

    std::string重载了一系列关系运算符,包括==!=<<=>>=。这些运算符可以按照字典序对两个字符串进行比较

    #include <iostream>
    #include <string>
    
    int main() {
        std::string str1 = "apple";
        std::string str2 = "banana";
        
        if (str1 < str2) {
            std::cout << str1 << " comes before " << str2;  // 输出:apple comes before banana
        }
        
        if (str1 != str2) {
            std::cout << "\nThey are not equal";  // 输出:They are not equal
        }
        return 0;
    }

    汇总:                                                                                                                                                

    int main()
    {
    	string str1 = "Hello";
    	string str2 = " World";
    	string result = str1 + str2;  // 连接两个字符串
    	result = result + '!';             // 连接字符串和字符
    	cout << result<<endl;
    
    
    	string name;
    	cout << "Enter your name: ";
    	cin >> name;  // 输入"Aramae Doe",只会读取"John"
    	cout << "Hello, " << name;  // 输出:Hello, Aramae
    
    	string address;
    	cout << "Enter your address: ";
    	getline(std::cin, address);  // 可以读取包含空格的整行内容
    	cout << "Your address: " << address;
    
    	string str1 = "apple";
    	string str2 = "banana";
    	if (str1 < str2) {
    		std::cout << str1 << " comes before " << str2;  // 输出:apple comes before banana
    	}
    	if (str1 != str2) {
    		std::cout << "\nThey are not equal";  // 输出:They are not equal
    	}
    
    	return 0;
    }

     上面的几个接口大家了解一下,下面的OJ题目中会有一些体现他们的使用。string类中还有一些其他的 操作,这里不一一列举,大家在需要用到时不明白了查文档即可


    2.7 . vs和g++下string结构的说明 

    注意:下述结构是在32位平台下进行验证,32位平台下指针占4个字节。
    2.71 vs下string的结构
    string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义string中字
    符串的存储空间
    • 当字符串长度小于16时,使用内部固定的字符数组来存放
    • 当字符串长度大于等于16时,从堆上开辟空间
    union _Bxty
    { // storage for small buffer or pointer to larger one
     value_type _Buf[_BUF_SIZE];
     pointer _Ptr;
     char _Alias[_BUF_SIZE]; // to permit aliasing
    } _Bx;
    这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建好之后,内
    部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。
    其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量
    最后:还有一个指针做一些其他事情。
    故总共占16+4+4+4=28个字节。
    2.72 g++下string的结构
    G++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指
    针将来指向一块堆空间,内部包含了如下字段:
    • 空间总大小
    • 字符串有效长度
    • 引用计数
    struct _Rep_base
    {
     size_type _M_length;
     size_type _M_capacity;
     _Atomic_word _M_refcount;
    };
    • 指向堆空间的指针,用来存储字符串。

    结语:感谢相遇

    /// 高山仰止,景行行止。虽不能至,心向往之 ///