C++ string类

发布于:2024-05-03 ⋅ 阅读:(22) ⋅ 点赞:(0)

目录

0.前言

1.为什么学习string类

1.1 C语言字符串的局限性

1.2 C++ string类的优势

2.标准库中的string类

2.1 字符串作为字符序列的类

2.2 接口与标准容器类似

2.3 基于模板的设计

2.4 编码和字符处理

3.string类的常用接口说明

3.1构造函数

3.1.1默认构造函数

3.1.2拷贝构造函数

3.1.3子字符串构造函数

3.1.4从C字符串构造

3.1.5从字符序列构造

3.1.6填充构造函数

3.2 容量操作函数

3.2.1 size() 和 length()

 3.2.2 max_size()

3.2.3 resize()

 3.2.4 capacity()

 3.2.5 reserve()

 3.2.6 clear()

 3.2.7 empty()

3.3访问及遍历操作

3.3.1 operator[]

3.3.2 迭代器

3.3.3 范围for

3.4类对象修改操作

3.4.1 operator+=

3.4.2 append

3.4.3 push_back

3.4.4 assign

3.4.5 insert

3.4.6 erase

3.4.7 replace 

3.4.8 swap

3.4.9 pop_back

4.小结


(图像由AI生成) 

0.前言

在现代软件开发中,文本处理是不可或缺的一部分。C++作为一种强大的编程语言,提供了多种方式来处理字符串。在本文中,我们将深入探讨C++标准库中的string类,了解其设计理念、功能以及使用方式。

1.为什么学习string类

学习C++中的string类对于任何使用C++进行软件开发的程序员来说都是至关重要的,尤其是从C语言的传统字符串处理转变而来的开发者。以下几点阐述了学习string类的重要性:

1.1 C语言字符串的局限性

C语言中的字符串处理依赖于字符数组和一系列标准库函数(如strcpy, strcat, strlen等)。这些方法虽然基本能满足需求,但存在几个显著的缺点:

  1. 安全性问题:C语言中的字符串操作常常涉及直接的内存操作,这容易造成缓冲区溢出、内存泄露等安全问题。例如,strcpy函数在复制数据时不会检查目标缓冲区的大小,这可能导致超出其容量的写操作。

  2. 效率低下:C语言的字符串操作通常需要手动管理字符串长度和内存,如需拼接字符串时,可能需要多次调用strlen来获取当前长度,然后再添加新内容,这样的操作效率较低。

  3. 使用不便:C语言的字符串需要程序员对内存非常小心地操作,容易出错且代码难以维护。

1.2 C++ string类的优势

与C语言的基本字符串处理相比,C++的string类提供了一个更安全、更高效、更易用的字符串操作方式:

  1. 自动内存管理string类自动管理内存,使用者不需要关心内存分配和释放的问题,极大地降低了内存泄漏和缓冲区溢出的风险。

  2. 丰富的成员函数string类内置了大量的成员函数,如append, find, replace, substr等,使得字符串的处理更为方便和直观。

  3. 动态大小string对象可以根据需要动态增长和缩小,无需预先声明最大容量,这一点与静态大小的C语言字符数组形成鲜明对比。

  4. 操作符重载和迭代器支持string类重载了多个操作符(如+=等),支持迭代器,使得字符串操作更符合C++的对象操作习惯,也支持现代C++中的范围for循环和算法库函数。

  5. 兼容性和灵活性:尽管string以字节为单位进行操作,对于多字节字符集的支持可能有限,但它在大多数情况下能够兼容处理UTF-8等编码,尤其是在使用支持这些编码的库时。

2.标准库中的string类

C++的string类是标准库中一个非常重要的组成部分,主要用于表示和操作字符序列。它被设计为易于使用且功能强大,以适应现代软件开发中对文本处理的复杂需求。以下是对string类详细介绍的几个关键方面。如果想要进一步地了解string的详细特性与使用方法,可以参考官方文档,详见:string - C++ Reference (cplusplus.com)

2.1 字符串作为字符序列的类

string类本质上是一个用于表示字符序列的容器。它封装了字符数组的许多复杂操作,提供了一种更安全、更直观的方式来处理文本数据。用户不需要关心底层的字符数组和内存管理,所有这些都由string类自动处理。

2.2 接口与标准容器类似

string类的接口设计借鉴了标准STL容器的模式,例如vectordeque。这意味着string提供了类似于这些容器的多种成员函数,如迭代器支持、元素访问、修改操作、容量查询等。这种设计使得string类既熟悉又易于使用对于已经熟悉其他C++标准容器的开发者。

2.3 基于模板的设计

string类实际上是basic_string模板的一个特化实例,使用char类型作为字符类型。basic_string是一个模板类,可以用不同的字符类型来实例化,如wchar_tchar16_tchar32_t等,对应的实例分别可以处理宽字符或Unicode字符。

  • basic_string<char>被typedef为string
  • basic_string<wchar_t>被typedef为wstring

这种模板设计提供了极大的灵活性,允许string类以几乎相同的方式处理不同类型的字符数据。

2.4 编码和字符处理

尽管string是以单字节字符char进行操作,这使得它在默认情况下适用于ASCII编码的字符串处理,但对于多字节字符集(如UTF-8)的支持则需要更多注意。由于string按字节操作,对于包含多字节字符的字符串,如UTF-8编码的文本,长度和位置的计算可能与字符实际显示的数量不同。这意味着在处理多字节字符集时,开发者可能需要使用额外的库或函数来正确解析和处理这些字符。

3.string类的常用接口说明

3.1构造函数

default (1)
string();
copy (2)
string (const string& str);
substring (3)
string (const string& str, size_t pos, size_t len = npos);
from c-string (4)
string (const char* s);
from sequence (5)
string (const char* s, size_t n);
fill (6)
string (size_t n, char c);

string类提供了多种构造函数,允许以不同的方式创建和初始化字符串对象。下面详细介绍这些构造函数的用法和适用场景,结合示例代码进行说明。

3.1.1默认构造函数

string();

默认构造函数创建一个空的字符串。这是最简单的构造函数,用于初始化时不提供任何字符内容的情况。

std::string str1; // 创建一个空字符串

3.1.2拷贝构造函数

string(const string& str);

拷贝构造函数用于创建一个新的字符串,其内容是另一个已存在的string对象的副本。这个函数在复制字符串或函数返回字符串时经常使用。

std::string original = "Hello"; 
std::string copy(original); // 使用拷贝构造函数

3.1.3子字符串构造函数

string(const string& str, size_t pos, size_t len = npos);

这个构造函数从一个现有的string对象创建一个新字符串,开始于指定的pos位置,长度为len。如果lennpos(默认值),则复制从pos开始到原字符串结束的部分。

std::string text = "Hello world"; 
std::string sub(text, 6, 5); // 从位置6开始,长度为5,结果为"world"

3.1.4从C字符串构造

string(const char* s);

这个构造函数使用C风格的字符串(以null终止的字符数组)初始化string对象。它复制字符直到遇到null字符。

const char* cstr = "C++ string"; 
std::string str2(cstr); // 使用C字符串构造,结果为"C++ string"

3.1.5从字符序列构造

string(const char* s, size_t n);

这个构造函数从指定的字符数组创建一个字符串,复制前n个字符。这适用于从部分字符数组构建字符串的情况。

const char* data = "Example"; 
std::string part(data, 4); // 只取前4个字符,结果为"Exam"

3.1.6填充构造函数

string(size_t n, char c);

创建一个字符串,其中包含n个重复的字符c。这个构造函数在需要初始化固定长度和内容的字符串时非常有用。

std::string filled(5, 'a'); // 创建一个包含5个'a'的字符串,结果为"aaaaa"

3.2 容量操作函数

string 类提供了一组容量操作函数,允许开发者查询和修改字符串的存储特性。这些功能对于优化性能和管理内存非常重要。以下是这些函数的详细介绍和示例代码:

3.2.1 size() 和 length()

size_t size() const;
size_t length() const;

这两个函数都返回字符串当前的长度,即字符串中字符的数量。在 std::string 中,size()length() 是等价的,提供了相同的功能。

std::string str = "Hello, world!";
std::cout << "Length of string: " << str.length() << std::endl; // 输出 13
std::cout << "Size of string: " << str.size() << std::endl;     // 同样输出 13

 3.2.2 max_size()

size_t max_size() const;

max_size() 函数返回字符串可能的最大长度,这是由于系统或库实现的限制决定的。这不是一个固定值,而是表示字符串可以达到的最大理论长度。

std::string str; 
std::cout << "Maximum size of string: " << str.max_size() << std::endl; // 输出可能的最大长度

3.2.3 resize()

void resize(size_t n);
void resize(size_t n, char c);

resize() 函数更改字符串的长度。如果新的长度大于当前长度,新位置将用字符 c 填充(如果提供了 c 的话)。如果新的长度小于当前长度,多余的字符将被截断。

std::string str = "Hello";
str.resize(10, 'x'); // "Helloxxxxx"
str.resize(3);       // "Hel"

 3.2.4 capacity()

size_t capacity() const;

capacity() 函数返回为字符串分配的存储空间的大小,通常这个值大于或等于 size()。这可以给出底层数组可能的空间大小,有助于了解内存使用情况。

std::string str = "Test";
std::cout << "Capacity of string: " << str.capacity() << std::endl; // 输出分配的内存大小

 3.2.5 reserve()

void reserve(size_t n = 0);

reserve() 函数试图改变字符串的容量,即预先分配足够的内存来存储至少 n 个字符,避免多次增加字符串大小时的重复分配。

std::string str;
str.reserve(100); // 为存储至少100个字符预分配内存
std::cout << "New capacity after reserve: " << str.capacity() << std::endl;

 3.2.6 clear()

void clear() noexcept;

clear() 函数删除字符串中的所有字符,使其长度变为0。这个函数不改变容量。

std::string str = "Something";
str.clear(); // 清空字符串
std::cout << "String after clear: '" << str << "'" << std::endl; // 输出 ''

 3.2.7 empty()

bool empty() const;

empty() 函数检查字符串是否为空(即长度为0)。如果字符串为空,返回 true,否则返回 false

std::string str;
std::cout << "Is the string empty? " << (str.empty() ? "Yes" : "No") << std::endl; // 输出 'Yes'

3.3访问及遍历操作

std::string 类中,访问和遍历字符串的方法包括使用下标操作符、迭代器和范围 for 循环。这些方法提供了灵活的方式来访问和遍历字符串中的字符。

3.3.1 operator[]

下标操作符 operator[] 允许你访问字符串中的特定位置的字符。这种访问方式是随机访问,时间复杂度为 O(1)。

std::string str = "Hello, world!";
char ch1 = str[0];  // 访问第一个字符,ch1 = 'H'
char ch2 = str[7];  // 访问第八个字符,ch2 = 'w'

std::cout << "First character: " << ch1 << std::endl;
std::cout << "Eighth character: " << ch2 << std::endl;

// 修改字符串中的字符
str[5] = '!';
std::cout << "Modified string: " << str << std::endl;  // 输出 "Hello! world!"

3.3.2 迭代器

std::string str = "Hello";
std::cout << "Characters in string: ";

// 使用正向迭代器遍历字符串
for (auto it = str.begin(); it != str.end(); ++it) {
    std::cout << *it << ' ';
}
std::cout << std::endl;

// 使用反向迭代器遍历字符串
std::cout << "Characters in reverse: ";
for (auto rit = str.rbegin(); rit != str.rend(); ++rit) {
    std::cout << *rit << ' ';
}
std::cout << std::endl;

 

迭代器提供了一种方式来顺序访问容器中的每个元素。std::string 支持正向和反向迭代器(begin/endrbegin/rend)。

3.3.3 范围for

范围 for 循环是 C++11 引入的一个特性,它允许简洁地遍历容器中的所有元素。在使用范围 for 循环遍历 std::string 时,可以直接访问每个字符。

std::string str = "world";

std::cout << "Characters in string using range-for: ";
for (char c : str) {
    std::cout << c << ' ';
}
std::cout << std::endl;

// 修改字符串中的字符(需要使用引用)
for (char &c : str) {
    c = toupper(c);  // 将字符转换为大写
}
std::cout << "Modified string: " << str << std::endl;  // 输出 "WORLD"

3.4类对象修改操作

std::string 类提供了多种方法来修改字符串的内容,这些方法包括添加、删除、插入和替换字符等操作。下面是这些方法的详细介绍和使用示例:

3.4.1 operator+=

void operator+=(const string& str);
void operator+=(char c);
void operator+=(const char* s);

operator+= 用于将字符串、字符或C字符串附加到现有的 std::string 对象上。

std::string str = "Hello";
str += ", world!";
std::cout << str << std::endl;  // 输出 "Hello, world!"

3.4.2 append

string& append(const string& str);
string& append(const string& str, size_t subpos, size_t sublen);
string& append(const char* s);
string& append(const char* s, size_t n);
string& append(size_t n, char c);

append 方法添加字符串、字符数组或多个相同字符到现有字符串的末尾。

std::string str = "Hello";
str.append(" world", 6);  // 附加了 " world" 中的前6个字符
std::cout << str << std::endl;  // 输出 "Hello world"

3.4.3 push_back

void push_back(char c);

push_back 将一个字符追加到字符串的末尾。

std::string str = "Hello"; 
str.push_back('!'); 
std::cout << str << std::endl; // 输出 "Hello!"

3.4.4 assign

string& assign(const string& str);
string& assign(const string& str, size_t subpos, size_t sublen);
string& assign(const char* s);
string& assign(const char* s, size_t n);
string& assign(size_t n, char c);

assign 方法用于将一个新的字符串内容赋给 std::string 对象,替换原有的内容。

std::string str;
str.assign("Hello world");
std::cout << str << std::endl;  // 输出 "Hello world"

3.4.5 insert

string& insert(size_t pos, const string& str);
string& insert(size_t pos, const char* s);

insert 方法在指定位置插入字符串或字符。

std::string str = "Hello world";
str.insert(6, "beautiful ");
std::cout << str << std::endl;  // 输出 "Hello beautiful world"

3.4.6 erase

string& erase(size_t pos = 0, size_t len = npos);

erase 方法从字符串中删除指定位置和长度的字符。

std::string str = "Hello beautiful world";
str.erase(5, 10);  // 删除从位置5开始的10个字符
std::cout << str << std::endl;  // 输出 "Hello world"

3.4.7 replace 

string& replace(size_t pos, size_t len, const string& str);
string& replace(size_t pos, size_t len, const char* s);

replace 方法替换字符串中指定位置和长度的部分为新的内容。

std::string str = "Hello beautiful world";
str.replace(6, 9, "awesome");  // 替换从位置6开始的9个字符
std::cout << str << std::endl;  // 输出 "Hello awesome world"

3.4.8 swap

void swap(string& str);

swap 方法交换两个字符串的内容。

std::string str1 = "Hello";
std::string str2 = "World";
str1.swap(str2);
std::cout << "str1: " << str1 << " str2: " << str2 << std::endl;  // 输出 "str1: World str2: Hello"

3.4.9 pop_back

void pop_back();

pop_back 删除字符串的最后一个字符。

std::string str = "Hello!";
str.pop_back();
std::cout << str << std::endl;  // 输出 "Hello"

4.小结

在本篇博客中,我们深入探讨了C++标准库中的string类,从其构造函数、容量操作函数,到访问及遍历操作,以及类对象的修改操作。string类作为C++中处理字符串的核心工具,提供了丰富的接口来高效、安全地管理和操作字符串。掌握这些功能不仅可以提高编程效率,还能帮助开发者编写更加健壮和可维护的代码,有效地处理现代软件开发中的文本数据挑战。