C++STL详解(一):string类

发布于:2025-07-09 ⋅ 阅读:(22) ⋅ 点赞:(0)

有关string类文档介绍详解

一、auto和范围for

在这里补充2个C++11的小语法,方便我们后面的学习。

1、auto关键字

  • 在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,后来这个不重要了。C++11中,标准委员会变废为宝赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得
  • 用auto 声明指针类型时,用auto和auto*没有任何区别,但用auto 声明引用类型时 则必须加&。
  • 当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
  • auto不能作为函数的参数,可以做返回值,但是建议谨慎使用。
  • auto不能直接用来声明数组。
#include<iostream>
using namespace std;
int func1()
{
	return 10;
}

 // 不能做参数
void func2(auto a)
{}

// 可以做返回值,但是建议谨慎使用
auto func3()
{
	return 3;
} 

int main()
{
	int a = 10;
	auto b = a;
	auto c = 'a';
	auto d = func1();
	
	// 编译报错:rror C3531: “e”: 类型包含“auto”的符号必须具有初始值设定项
	auto e;
	cout << typeid(b).name() << endl;
	cout << typeid(c).name() << endl;
	cout << typeid(d).name() << endl;
	int x = 10;
	auto y = &x;
	auto* z = &x;
	auto& m = x;
	cout << typeid(x).name() << endl;
	cout << typeid(y).name() << endl;
	cout << typeid(z).name() << endl;
	auto aa = 1, bb = 2;
	
	// 编译报错:error C3538: 在声明符列表中,“auto”必须始终推导为同一类型
	auto cc = 3, dd = 4.0;
	
	// 编译报错:error C3318: “auto []”: 数组不能具有其中包含“auto”的元素类型
	auto array[] = { 4, 5, 6 };
	return 0;
}
#include<iostream>
#include <string>
#include <map>
using namespace std;
int main()
{
	std::map<std::string, std::string> dict = 
	{ { "apple", "苹果" },{ "orange","橙子" }, {"pear","梨"} };
	// auto的用武之地
	//std::map<std::string, std::string>::iterator it = dict.begin();
	auto it = dict.begin();
	while (it != dict.end())
		{
			cout << it->first << ":" << it->second << endl;
			++it;
		}
	return 0}

这段代码通过 std::map 存储了三个水果名称及其对应的中文翻译,使用迭代器遍历并打印出每一个键值对。 auto 使得迭代器的类型自动推导,简化了代码。

2、范围for

  • 对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束。
  • 范围for可以作用到数组和容器对象上进行遍历。
  • 范围for的底层很简单,容器遍历实际就是替换为迭代器,这个从汇编层也可以看到。
#
include<iostream>
#include <string>
#include <map>
using namespace std;
int main()
{
	int array[] = { 1, 2, 3, 4, 5 };
	// C++98的遍历
	for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
		{
			array[i] *= 2;
		} 
	for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
		{
			cout << array[i] << endl;
		}
	// C++11的遍历
	for (auto& e : array)
		e *= 2;
	for (auto e : array)
		cout << e << " " << endl;
		
	string str("hello world");
	for (auto ch : str)
		{
			cout << ch << " ";
		} 
	cout << endl;
return 0;
}

二、string类的常用接口说明(注意下面我只讲解最常用的接口)

1. string类对象的常见构造

  1. string()(空字符串构造)
#include <iostream>
#include <string>
using namespace std;

int main()
{
    string s1;  // 调用默认构造函数,构造一个空字符串
    cout << "s1: " << s1 << endl;  // 输出空内容
    cout << "s1.size(): " << s1.size() << endl;  // 输出 0
    return 0;
}

功能说明:
构造一个空的 string 对象,不包含任何字符。

注意事项:
默认初始化后,size() 返回 0,但可以通过 push_back 或 += 动态添加字符。

  1. string(const char* s)(C字符串构造)
#include <iostream>
#include <string>
using namespace std;

int main() 
{
    const char* cstr = "Hello, C++";
    string s2(cstr);  // 用C风格字符串构造string对象
    cout << "s2: " << s2 << endl;  // 输出 "Hello, C++"
    return 0;
}

功能说明:
通过C风格字符串(const char*)初始化 string 对象。

注意事项:
需确保传入的指针指向有效的以 \0 结尾的字符串,否则行为未定义。

  1. string(const string& s)(拷贝构造)
#include <iostream>
#include <string>
using namespace std;

int main() {
    string original = "Copy this";
    string copy(original);  // 调用拷贝构造函数
    cout << "copy: " << copy << endl;  // 输出 "Copy this"
    return 0;
}

功能说明:
通过另一个 string 对象构造新对象,内容与原对象完全一致。

注意事项:
深拷贝实现,修改原对象不会影响新对象。

总结:
在这里插入图片描述

2、string类对象的容量操作

  1. size()(返回有效字符长度)
#include <iostream>
#include <string>
using namespace std;

int main() 
{
    string s = "Hello";
    cout << "s.size(): " << s.size() << endl;  // 输出 5
    return 0;
}

功能说明:
返回字符串中当前存储的有效字符数量(不包含结尾的 \0)。

注意事项:
与 length() 功能完全相同,但 size() 是STL容器的通用接口,推荐优先使用。

  1. empty()(检查字符串是否为空)
#include <iostream>
#include <string>
using namespace std;

int main() 
{
    string s1 = "abc";
    string s2;
    cout << "s1.empty(): " << s1.empty() << endl;  // 输出 0(false)
    cout << "s2.empty(): " << s2.empty() << endl;  // 输出 1(true)
    return 0;
}

功能说明:
若字符串长度为0(即 size() == 0),返回 true,否则返回 false。

注意事项:
比手动检查 size() == 0 更直观,推荐使用。

  1. clear()(清空字符串)
#include <iostream>
#include <string>
using namespace std;

int main() 
{
    string s = "Hello";
    s.clear();
    cout << "After clear, s: " << s << endl;       // 输出空
    cout << "s.size(): " << s.size() << endl;      // 输出 0
    cout << "s.capacity(): " << s.capacity() << endl; // 容量可能不变
    return 0;
}

功能说明:
清空字符串内容,但不释放内存(capacity() 可能保持不变)

注意事项:
若需彻底释放内存,可结合 shrink_to_fit()(C++11)使用。

  1. reserve()(预留空间)
#include <iostream>
#include <string>
using namespace std;

int main() 
{
    string s;
    s.reserve(100);  // 预分配至少100字节的空间
    cout << "s.capacity(): " << s.capacity() << endl; // 可能 >=100
    cout << "s.size(): " << s.size() << endl;         // 输出 0(内容仍为空)
    return 0;
}

功能说明:
提前分配内存以减少后续动态扩容的开销。

注意事项:
实际容量可能大于请求值(由实现决定)。
仅影响容量,不改变内容或长度。

  1. resize()(调整有效字符数量)
#include <iostream>
#include <string>
using namespace std;

int main() 
{
    string s = "Hello";
    s.resize(8, 'x');  // 扩展至8字符,不足部分用'x'填充
    cout << "s: " << s << endl;           // 输出 "Helloxxx"
    s.resize(3);       // 截断至3字符
    cout << "s: " << s << endl;           // 输出 "Hel"
    return 0;
}

功能说明:
调整字符串长度:
若 n > size(),填充指定字符(默认 \0)。
若 n < size(),截断多余字符。

注意事项:

  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, charc)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
  4. eserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小。

总结:
在这里插入图片描述

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

  1. operator[](下标访问字符)
#include <iostream>
#include <string>
using namespace std;

int main() 
{
    string s = "Hello";
    
    // 非const对象:可读可写
    s[0] = 'h';                  // 修改第一个字符
    cout << s[0] << endl;        // 输出 'h'(读取第一个字符)

    // const对象:只读
    const string cs = "World";
    cout << cs[2] << endl;       // 输出 'r'(只能读取)
    // cs[0] = 'w';             // 错误!const对象不可修改
    return 0;
}

功能说明:
通过下标直接访问指定位置的字符(从 0 开始)。

注意事项:
不检查越界(若 pos >= size(),行为未定义)。
对 const string 返回 const char&,禁止修改。

  1. begin() 和 end()(正向迭代器)
#include <iostream>
#include <string>
using namespace std;

int main() 
{
    string s = "C++";
    
    // 使用迭代器遍历
    for (auto it = s.begin(); it != s.end(); ++it) 
    {
        cout << *it << " ";      // 输出:C + + 
    }
    cout << endl;

    // 修改内容
    *s.begin() = 'c';           // 将首字符改为 'c'
    cout << s << endl;           // 输出 "c++"
    return 0;
}

功能说明:
begin() 返回指向首字符的迭代器,end() 返回末尾的“哨兵”迭代器(指向最后一个字符的下一个位置)。

注意事项:
若字符串为空,begin() == end()。
支持通过迭代器修改字符(除非是 const 迭代器)。

  1. rbegin() 和 rend()(反向迭代器)
#include <iostream>
#include <string>
using namespace std;

int main() 
{
    string s = "12345";
    
    // 反向遍历
    for (auto rit = s.rbegin(); rit != s.rend(); ++rit) 
    {
        cout << *rit << " ";     // 输出:5 4 3 2 1
    }
    cout << endl;
    return 0;
}

功能说明:
rbegin() 返回指向最后一个字符的反向迭代器,rend() 返回首字符前一个位置的迭代器。

注意事项:
反向迭代器的 ++ 操作会向字符串开头移动。

  1. 范围 for 循环(C++11)
#include <iostream>
#include <string>
using namespace std;

int main() 
{
    string s = "Range";
    
    // 简洁遍历(只读)
    for (char ch : s) 
    {
        cout << ch << " ";       // 输出:R a n g e
    }
    cout << endl;

    // 修改内容需用引用
    for (char& ch : s) 
    {
        ch = toupper(ch);       // 转为大写
    }
    cout << s << endl;           // 输出 "RANGE"
    return 0;
}

功能说明:
语法上,底层通过 begin() 和 end() 实现。

注意事项:
若需修改元素,需使用 char& 引用类型。

总结:在这里插入图片描述

4、string类对象的修改操作

  1. operator+=(追加字符串/字符)
#include <iostream>
#include <string>
using namespace std;

int main() 
{
    string s = "Hello";
    s += " World";      // 追加字符串
    s += '!';           // 追加字符
    cout << s << endl;  // 输出 "Hello World!"
    return 0;
}

功能说明:
支持追加另一个 string、C风格字符串或单个字符。

注意事项:
直接修改原字符串,是最常用的拼接方式(高效且简洁)。

  1. c_str()(返回C风格字符串)
#include <iostream>
#include <string>
#include <cstring>
using namespace std;

int main() 
{
    string s = "C++";
    const char* p = s.c_str();  // 获取C风格字符串指针
    
    cout << "C-string: " << p << endl;          // 输出 "C++"
    cout << "Length via strlen: " << strlen(p) << endl;  // 输出 3
    
    // 注意:若s被修改,p可能失效
    s += "11";
    // cout << p;  // 危险!p可能指向无效内存
    return 0;
}

功能说明:
返回 const char* 指针,指向以 \0 结尾的字符数组。

注意事项:
不要存储返回的指针,若原 string 被修改,指针可能失效(如扩容后)。
常用于需要C风格字符串的接口(如文件操作、C库函数)。

  1. find() 和 npos(查找字符/子串)
#include <iostream>
#include <string>
using namespace std;

int main() 
{
    string s = "Hello, C++!";
    size_t pos;
    
    // 查找字符
    pos = s.find('C');
    if (pos != string::npos) 
    {
        cout << "'C' found at: " << pos << endl;  // 输出 7
    }

    // 查找子串
    pos = s.find("C++");
    if (pos != string::npos)
    {
        cout << "\"C++\" found at: " << pos << endl;  // 输出 7
    }

    // 查找不存在的字符
    pos = s.find('x');
    if (pos == string::npos) 
    {
        cout << "'x' not found!" << endl;  // 输出此提示
    }
    return 0;
}

功能说明:
find() 返回首次匹配的位置(从 0 开始),失败时返回 string::npos。
可查找字符或子串,支持指定起始位置(如 s.find(‘o’, 5))。

注意事项:
检查返回值时必须使用 npos,不可直接与 -1 比较(npos 是无符号类型)。
rfind() 为反向查找(从后往前)。

  1. substr()(提取子串)
#include <iostream>
#include <string>
using namespace std;

int main() 
{
    string s = "Programming";
    string sub = s.substr(3, 4);  // 从位置3开始,截取4个字符
    cout << sub << endl;          // 输出 "gram"

    // 省略长度参数:截取到末尾
    sub = s.substr(7);
    cout << sub << endl;          // 输出 "ming"
    return 0;
}

功能说明:
返回从 pos 开始、长度为 n 的子串(默认 n 到末尾)。

注意事项:
1. 在string尾部追加字符时,s.push_back© / s.append(1, c) / s += 'c’三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。
2. 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好

总结:
在这里插入图片描述

5、string类非成员函数

  1. operator>> 和 operator<<(输入/输出运算符重载)
#include <iostream>
#include <string>
using namespace std;

int main() 
{
    string s;
    
    // 输入示例(遇到空格/换行停止)
    cout << "Enter a word: ";
    cin >> s;                   // 输入 "Hello World"
    cout << "You entered: " << s << endl;  // 输出 "Hello"(空格截断)

    // 输出示例
    string name = "Alice";
    cout << "Name: " << name << endl;    // 输出 "Name: Alice"
    return 0;
}

功能说明:
从流中读取数据到 string,默认以空格/换行分隔。
将 string 输出到流。

注意事项:
会忽略前导空白符,读取到下一个空白符为止。
需配合 getline() 读取整行(见下文)。

  1. getline()(获取整行输入)
#include <iostream>
#include <string>
using namespace std;

int main()
{
    string line;
    
    cout << "Enter a line: ";
    getline(cin, line);          // 读取整行(包括空格)
    cout << "Line: " << line << endl;  // 输出完整输入

    // 指定分隔符(默认'\n')
    getline(cin, line, ';');    // 读取直到遇到分号
    cout << "Until ';': " << line << endl;
    return 0;
}

功能说明:
从输入流中读取一行(默认以 \n 结尾),可指定分隔符。

注意事项:
与 cin >> 混用时,需先清除缓冲区残留的换行符:
cin.ignore(); // 忽略前一个输入的换行

  1. relational operators(比较运算符)
#include <iostream>
#include <string>
using namespace std;

int main() 
{
    string a = "apple";
    string b = "banana";
    
    // 比较示例(按字典序)
    cout << boolalpha;
    cout << "a < b: " << (a < b) << endl;   // 输出 true('a' < 'b')
    cout << "a == \"apple\": " << (a == "apple") << endl;  // 输出 true

    // 支持所有比较运算符:>, >=, <=, !=
    return 0;
}

功能说明:
按字符的字典序(ASCII值)逐位比较。

注意事项:
比较时区分大小写(“A” < “a” 为真)。
可直接与C风格字符串比较(如 s == “text”)。

  1. operator+(不推荐使用)
string s1 = "Hello", s2 = "World";
string s3 = s1 + " " + s2;  // 拼接,但每次+生成临时对象

为什么不推荐?
每次 + 操作会生成临时对象,频繁拼接时效率低(尤其是循环中)。
替代方案:
使用 += 或 append() 直接修改原字符串:

string s;
s += s1;
s += " ";
s += s2;  // 无额外拷贝

总结:
在这里插入图片描述

三、vs和g++下string结构的说明

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

1、vs下string的结构

string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义。

string中字符串的存储空间:

  • 当字符串长度小于16时,使用内部固定的字符数组来存放
  • 当字符串长度大于等于16时,从堆上开辟空间

这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建好之后,内部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。
其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量。
最后:还有一个指针做一些其他事情。故总共占16+4+4+4=28个字节。
在这里插入图片描述

2、g++下string的结构

G++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指针将来指向一块堆空间,内部包含了如下字段:

  • 空间总大小
  • 字符串有效长度
  • 引用计数
  • 指向堆空间的指针,用来存储字符串

网站公告

今日签到

点亮在社区的每一天
去签到