1. 为什么要学习string类?

在 C 语言里,字符串被定义为 以字符’\0’作为结尾标志的一组字符集合。 这种定义方式使得字符串在存储识别上有了明确的界定。 为了让开发者能更便捷地对字符串进行诸如:复制拼接比较等操作,C 标准库精心提供了一系列以str开头的库函数,例如

  • strcpy(字符串复制函数)
  • strcat(字符串拼接函数)
  • strcmp(字符串比较函数)
  • ……

然而:这些库函数存在一定的局限性,它们与字符串本身在逻辑上是相互分离的。

  • 从面向对象编程(OOP)的理念来看,OOP 强调将数据和操作数据的方法封装在一起,形成一个有机的整体,这样可以让代码的结构更加清晰,可维护性更强。
  • 但 C 标准库中的str系列库函数并非如此,它们独立于字符串对象之外,不太契合 OOP 这种将数据与操作紧密结合的思想。

不仅如此:在使用这些库函数操作字符串时,底层的内存空间管理责任完全落在了用户身上。

  • 用户需要自行负责分配足够的空间来存储字符串以及确保在对字符串进行操作时不会超出所分配空间的边界。
  • 这就要求开发者在编写代码时格外小心谨慎,稍有疏忽,比如:在复制字符串时没有正确计算目标空间的大小,就极有可能导致越界访问的问题,进而引发程序崩溃、数据损坏等严重后果。

基于上述种种原因,我们会发现 C 语言中字符串操作存在一些不便之处,而 C++ 的 string 类能够很好地解决这些问题:

  • string类将字符串数据及其相关操作封装在一起,完美契合面向对象编程(OOP)的思想,使得代码结构更加清晰、易于维护。
  • string类内部会自动管理底层的内存空间,无需开发者手动操心内存分配与释放的细节,极大地降低了因内存管理不当导致越界访问等错误的风险。
2. 标准库中的string类是什么样的呢?

cplusplus网站上关于C++的string类的介绍 C++ Reference

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

标准库中的 string类相关知识主要可分为以下三大部分: 1. 成员函数

  • 迭代器相关:用于获取指向string类内部字符的迭代器。
    • 例如:方便对字符串进行遍历、查找等操作
  • 容器特性相关:实现类似容器的功能。
    • 例如:获取字符串长度、判断是否为空等
  • 元素访问:提供接口用于直接访问字符串中的字符。
    • 例如:通过下标访问特定位置的字符
  • 修改操作:用于改变字符串的内容。
    • 例如:插入、删除、替换字符或子串等
  • 字符串操作:比较字符串等操作。
    • 例如:字符串的拼接、查找子串

2. 非成员函数重载

  • 对一些常见运算符(+用于字符串拼接等 )以及输入输出流运算符(<<>> )进行重载。
  • 以支持与string类对象的自然交互。

3. 成员常量 string类中定义的一些具有固定值的常量,可能用于表示特定的属性边界条件等。

在这里插入图片描述

在这里插入图片描述

3. string类在MSVC和g++编译器下有什么区别?
3.1:结构上不同

注意:下述结构是在 32 位平台下验证的,在 32 位平台中指针占 4 个字节

在 MSVC(Microsoft Visual C++)环境下,string的结构相对复杂,总共占用28 个字节

其内部包含一个联合体,该联合体用于定义string中字符串的存储空间。

代码语言:javascript

AI代码解释

//联合体的定义如下:
union _Bxty 
{    
    // 用于存放小缓冲区或指向更大缓冲区的指针
    value_type _Buf[_BUF_SIZE]; 
  
    pointer _Ptr;
    char _Alias[_BUF_SIZE]; // 用于允许别名
} _Bx;
  • 当字符串长度 < 16 时:使用内部固定的字符数组来存放字符串。
  • 当字符串长度 >= 16 时:则从堆上开辟空间来存储。

这种设计是有道理的,在大多数情况下,字符串长度小于 16 。 此时,string对象创建后,内部已具备 16 个字符数组的固定空间,无需从堆上动态创建,提高了效率。

  • 此外,string结构中还有一个size_t类型字段,用于保存字符串的实际长度。
  • 另一个size_t类型字段,用来保存从堆上开辟空间的总容量。
  • 最后,还有一个指针,用于其他相关操作。

因此:string的总字节数为 16(联合体中字符数组的大小) + 4(保存长度的size_t字段) + 4(保存容量的size_t字段) + 4(指针大小) = 28 个字节。


在 g++ (GNU C++)环境下,string采用写时拷贝机制实现。string对象本身仅占4个字节

  • 内部仅有一个指针,该指针指向一块位于堆上的空间。

这块堆空间包含以下字段:

  • 字符串有效长度
  • 空间总容量
  • 引用计数

代码语言:javascript

AI代码解释

struct _Rep_base 
{
   size_type     _M_length;    //字符串有效长度
   size_type     _M_capacity;  //空间总容量
   _Atomic_word  _M_refcount;  //引用计数
};
3.2:扩容规则不同

MSVC(Microsoft Visual C++)的扩容规则:

  • 扩容因子:通常为 1.5 倍(非固定值,可能随版本调整)
  • 行为特点:相对保守的内存增长,减少内存浪费

代码语言:javascript

AI代码解释

int main()
{
	// reverse 反转  逆置
	// reserve 保留、预留

	//1.使用标准库中的string类实例化一个对象
	string s;

	//2.使用标准库中的string类封装的成员函数reserve:提前开空间,避免扩容,提高效率
	//s.reserve(100);  

	//3.使用标准库中的string类封装的成员函数capacity:获取当前的字符串的总容量
	size_t sz = s.capacity();
	cout << "capacity changed: " << sz << '\n';

	cout << "making s grow:\n";
	for (int i = 0; i < 100; ++i)
	{
		//3.使用标准库中的string类封装的成员函数push_back:向字符串的尾部添加字符(目的:观察一下MSVC环境下的string的扩容规则)
		s.push_back('c');
		if (sz != s.capacity())
		{
			sz = s.capacity();
			cout << "capacity changed: " << sz << '\n';
		}
	}

	return 0;
}

在这里插入图片描述

在这里插入图片描述

g++ (GNU C++)的扩容规则:

  • 扩容因子:通常为 2 倍(早期版本常见)
  • 行为特点:更激进的内存增长,减少频繁扩容的开销

-------------标准接口---------------

-----------成员函数-----------
1.常见构造

下面我们直接来看标准库中关于string类构造函数的介绍: 早期 C++ 设计者为string类设计了多达 9 个构造函数,目前我们先集中学习以下红色方框中的 4 个。 暂未学习其他构造函数,主要有两点原因:

  1. 部分构造函数实际应用场景较少,现阶段学习性价比不高,可暂不关注
  2. 部分构造函数涉及较复杂的知识(如:模板进阶等),对当前学习进度而言难度较大,适合后续阶段深入学习

构造函数名称

功能说明

string()

构造一个空的 string 类对象(即:空字符串)

string(const char* s)

用 C 风格字符串(C-string)构造 string 类对象

string(size_t n, char c)

构造包含 n 个相同字符 c 的 string 类对象

string(const string& s)

拷贝构造函数,用于复制另一个 string 对象

在这里插入图片描述

在这里插入图片描述

代码语言:javascript

AI代码解释

#include <iostream>
#include <string>
using namespace std;

void Teststring()
{
    // 使用string()构造空的string类对象s1
    cout << "使用string()构造函数" << endl;
    string s1;
    cout << "s1: " << s1 << endl;

    // 使用string(const char* s)用C-string来构造string类对象s2
    cout << "使用string(const char* s)构造函数" << endl;
    string s2("hello world");
    cout << "s2: " << s2 << endl;

    // 使用string(size_t n, char c)构造包含n个字符c的string类对象s3
    cout << "使用string(size_t n, char c)构造函数" << endl;
    string s3(5, 'a');
    cout << "s3: " << s3 << endl;

    // 使用string(const string& s)拷贝构造函数构造s4,以s2为蓝本
    cout << "使用string(const string& s)拷贝构造函数" << endl;
    string s4(s2);
    cout << "s4: " << s4 << endl;
}

int main()
{
    Teststring();
    return 0;
}

在这里插入图片描述

在这里插入图片描述

2.容量操作

:和上面一样这里我们还是只是挑选几个常用的进行介绍

在这里插入图片描述

在这里插入图片描述

函数名称

功能说明

备注

size()

返回字符串中有效字符的长度(不包含结尾的 \0)

与 length() 功能完全相同,推荐使用 size() 保持容器一致性

length()

返回字符串中有效字符的长度

历史遗留接口,行为与 size() 一致,通常用于字符串操作的可读性

capacity()

返回当前字符串分配的总空间大小(单位:字节)

总空间 ≥ 有效字符长度(size())

empty()

检查字符串是否为空(即:size() == 0)空则返回 true,否则返回 false

比手动检查 size() == 0 更直观高效

clear()

清空字符串中的所有有效字符(size() 变为 0),但不释放内存空间

清空后 capacity() 保持不变,适合复用对象避免重复分配内存

reserve(n)

预分配至少容纳 n 个字符的内存空间(可能扩容)

仅影响容量,不改变内容或长度若 n < capacity(),可能无操作

resize(n, c)

调整有效字符数量为 n 个,多出的空间用字符 c 填充若 n < size() 则截断

默认填充 \0(若:未指定 c)可能触发扩容(若:n > capacity())

std::string::size

在这里插入图片描述

在这里插入图片描述

std::string::length

在这里插入图片描述

在这里插入图片描述

std::string::capacity

在这里插入图片描述

在这里插入图片描述

代码语言:javascript

AI代码解释

#include <iostream>
#include <string>
using namespace std;

int main()
{
    string str("Test string");
    cout << "size: " << str.size() << "\n";
    cout << "length: " << str.length() << "\n";
    cout << "capacity: " << str.capacity() << "\n";

    return 0;
}

在这里插入图片描述

在这里插入图片描述

std::string::empty

在这里插入图片描述

在这里插入图片描述

代码语言:javascript

AI代码解释

#include <iostream>    
#include <string>     
using namespace std;  

int main() 
{
    //1.用于临时存储每一行输入的文本
    string line;
    //2.用于存储用户输入的完整文本内容
    string content;    
    
    cout << "请输入一段文本,输入空行以结束:\n";

    //3.循环读取用户输入的每一行,直到遇到空行为止
    do 
    {
        //3.1:从标准输入读取一行文本(包括空格),存入 line 中
        getline(cin, line); //注意:getline 会读取到换行符为止,但不会将换行符存入 line

        //3.2:将当前行追加到 content 中,并手动添加换行符(因为 getline 不保留换行符)
        content += line + '\n';

    } while (!line.empty()); //循环条件:只要当前行不为空(!line.empty())就继续循环

    //4.输出用户输入的完整文本(包含所有行,包括最后的空行)
    cout << "您输入的文本是:\n" << content;

    return 0;
}

在这里插入图片描述

在这里插入图片描述

std::string::clear

在这里插入图片描述

在这里插入图片描述

代码语言:javascript

AI代码解释

#include <iostream>
#include <string>
using namespace std;  

int main()
{
    //1.用于存储从输入流读取的单个字符
    char c;           
    //2.用于拼接读取到的字符,形成一行文本
    string str;           
    cout << "请输入几行文本以(.)作为结束标志\n";  

    //3.循环读取字符,直到读取到 '.' 时结束循环
    do 
    {
        //3.1:从标准输入流读取一个字符(包括空格、换行符等)
        c = cin.get();     
        //3.2:将读取到的字符追加到字符串str的末尾
        str += c;          

        //3.3:当读取到换行符 '\n' 时,表示一行输入结束
        if (c == '\n')
        {
            cout << str;   // 输出当前拼接好的一行文本(包含换行符)
            str.clear();   // 清空字符串str,准备接收下一行的输入
        }
    } while (c != '.'); 

    //4.输出读取到的文本内容
    cout << str << endl;
    return 0;
}

在这里插入图片描述

在这里插入图片描述

std::string::reserve

在这里插入图片描述

在这里插入图片描述

代码语言:javascript

AI代码解释

#include <iostream>
#include <string>
using namespace std;

int main()
{
	/*--------------------创建一个包含长字符串的string对象--------------------*/
	cout << "创建一个包含长字符串的string对象" << endl;
	string s2("hello worldxxxxxxxxxxxxx"); 
	cout << "长度:" << s2.size() << endl;
	cout << "容量:" << s2.capacity() << endl << endl;

	/*--------------------要求string保留至少20个字符的容量--------------------*/
	cout << "要求string保留至少20个字符的容量" << endl;
	s2.reserve(20); 
	cout << "长度:" << s2.size() << endl;              //长度不变
	cout << "容量:" << s2.capacity() << endl << endl;  //容量不变


	/*--------------------要求string保留至少28个字符的容量--------------------*/
	cout << "要求string保留至少28个字符的容量" << endl;
	s2.reserve(28);
	cout << "长度:" << s2.size() << endl;				//长度不变
	cout << "容量:" << s2.capacity() << endl << endl;  //容量可能增长


	/*--------------------要求string保留至少40个字符的容量--------------------*/
	cout << "要求string保留至少40个字符的容量" << endl;
	s2.reserve(40);
	cout << "长度:" << s2.size() << endl;				//长度不变
	cout << "容量:" << s2.capacity() << endl << endl;  //容量进一步增长


	/*--------------------清空字符串内容--------------------*/
	cout << "清空字符串内容" << endl;
	s2.clear();  //注意:这只会清除内容,不会释放已分配的内存
	cout << "长度:" << s2.size() << endl;				//长度为0
	cout << "容量:" << s2.capacity() << endl << endl;  //容量保持不变

	return 0;
}

在这里插入图片描述

在这里插入图片描述

std::string::resize

在这里插入图片描述

在这里插入图片描述

代码语言:javascript

AI代码解释

#include <iostream>   
#include <string>     
using namespace std;  

int main() 
{
    //1.初始化字符串str为"I like to code in C"
    string str("I like to code in C");
    cout << str << '\n';  //输出初始字符串

    //2.获取当前字符串的长度(字符数量),不包含结尾的空字符'\0'
    unsigned sz = str.size();


    //3.调整字符串长度:在原有长度基础上增加2个字符
    str.resize(sz + 2, '+'); //新增的字符用'+'填充
    cout << str << '\n';     //输出调整后的字符串(末尾多了两个'+')

    //4.再次调整字符串长度:将长度改为14个字符
    str.resize(14);  //注意:若新长度小于原长度,会截断字符串(只保留前14个字符)
    cout << str << '\n';  //输出截断后的字符串(前14个字符为"I like to code")

    return 0;
}

在这里插入图片描述

在这里插入图片描述

3.访问操作
std::string::operator[]

在这里插入图片描述

在这里插入图片描述

代码语言:javascript

AI代码解释

#include <iostream>   
#include <string>     
using namespace std;  

int main() 
{
    //1.初始化字符串str为"Test string"
    string str("Test string");

    //2.循环遍历字符串中的每个字符并输出
    for (int i = 0; i < str.length(); ++i) 
    {
        //2.1:使用 operator[] 访问字符串的第i个字符
        cout << str[i];
    }
    cout << endl;

    return 0;  
}

在这里插入图片描述

在这里插入图片描述

4.修改操作

在这里插入图片描述

在这里插入图片描述

函数名称

功能说明

参数说明

注意事项

push_back(c)

在字符串末尾插入单个字符 c

c: 要插入的字符(char类型)

效率高于+=追加单字符,但批量操作建议用append或operator+=

append(str)

在字符串末尾追加另一个字符串(支持string/char*/子串)

str: 要追加的字符串(支持多种重载形式)

比+=功能更灵活(如可追加部分子串或重复字符)

operator+=(str)

最常用的字符串追加方式(支持string/char*/字符)

str: 要追加的内容(支持字符串或单字符)

代码简洁,性能与append()相当,推荐日常使用

insert(pos, str)

在指定位置 pos 插入字符串 str(支持多种插入形式)

pos: 插入位置索引str: 要插入的内容(字符串 / 字符 / 子串等)

插入后原位置及后续字符自动后移pos 超出范围会抛出异常(at() 风格重载)或导致未定义行为([] 风格)

erase(pos, len)

从位置 pos 开始删除 len 个字符(len 省略时删除到末尾)

pos: 起始删除位置len: 要删除的字符数(可选)

若 pos 超出范围会抛出异常若 len 超出剩余字符数,仅删除到字符串末尾

关于 string 需注意以下两点: 1. 尾部追加字符的三种方式:

  • s.push_back(c):向string尾部添加单个字符c
  • s.append(1, c):明确指定追加 1 个字符c
  • s += 'c':简洁的运算符重载方式,既可以追加单个字符'c',也能直接连接字符串(如:"hello"

实际应用中+=操作因语法简洁、适用场景灵活(字符 / 字符串均可),是最常用的追加方式


 

更多推荐