string类及其模拟实现
- 1.我们为什么要学习string类
- 2. 标准库中的string类
- 2.1 string类的实例化标准
- 2.2 了解string
- 3.string类的常用接口说明
- 3.1 string类对象的常见构造
- 3.2 string类对象的容量操作
- 3.3 string类对象的元素访问
- 3.4 string类对象的Iterators(迭代器)接口
- 3.5 string类对象的修改器(Modifiers)
- 3.6 string类对象的操作函数(operations)
- 3.7 string类对象的非成员函数和npos
- 4.vs和g++下string结构的说明
- 4.1 vs下string的结构
- 4.2 g++下string的结构
- 4.3 vs和g++的string扩容机制
- 结语
1.我们为什么要学习string类
学习 string 类是在 C++ 中非常重要的一步,string 类是 C++ 标准库提供的用于处理字符串的类,它相比 C 语言中的字符串处理函数更为高级、灵活和安全。以下是学习 string 类的一些重要理由:
- 功能强大:string 类提供了丰富的成员函数和操作符,用于处理字符串的拼接、查找、替换、截取、插入等操作。使用 string 类能够更方便地实现复杂的字符串处理任务。
- 安全性:string 类自动处理字符串的内存分配和释放,不会像 C 语言中的字符数组那样容易出现缓冲区溢出等安全问题。使用 string 类能够有效避免许多与字符数组相关的安全漏洞。
- 可读性:使用 string 类可以使代码更加易读,不需要过多关注底层的字符处理,而是通过成员函数和操作符来操作字符串,使代码更加清晰明了。
- 便捷性:string 类可以直接进行赋值、比较和连接等操作,无需额外的库函数支持,让代码编写更为简洁。
- STL 通用性:string 类是 C++ 标准模板库(STL)中的一部分,学习 string 类也是为了更好地理解和使用 STL 的其他组件,如向量、链表、映射等。
- 实际应用广泛:在 C++ 开发中,字符串处理是一个常见的任务,学习 string 类可以使您在实际开发中更加得心应手。
总的来说,学习 string 类对于掌握 C++ 编程是非常重要的。它是处理字符串的常用工具,能够让你更高效地编写代码,提高代码的质量和可读性,并帮助你更好地应对实际开发中的字符串处理任务。
2. 标准库中的string类
2.1 string类的实例化标准
类的实例化标准分为string、u16string、u32string 和 wstring 四种,他们都是 C++ 中用于表示字符串的类,但它们的底层字符类型和编码方式不同:
string:
底层字符类型:char
编码方式:使用单字节编码,通常采用 ASCII 或 UTF-8 编码。
示例:std::string myString = "Hello, world!";
u16string:
底层字符类型:char16_t
编码方式:使用两个字节表示一个字符,通常采用 UTF-16 编码。
示例:std::u16string myString = u"Hello, world!";
u32string:
底层字符类型:char32_t
编码方式:使用四个字节表示一个字符,通常采用 UTF-32 编码。
示例:std::u32string myString = U"Hello, world!";
wstring:
底层字符类型:wchar_t
编码方式:宽字符编码,可以是 UTF-16 或 UTF-32,具体取决于平台。
示例:std::wstring myString = L"Hello, world!";
需要注意的是,不同编译器和平台对宽字符编码的实现可能不同。在 Windows 平台上,wchar_t 通常是 16 位的,而在一些其他平台上,它可能是 32 位的。因此,在使用 wstring 时需要注意平台的差异。
选择使用哪种字符串类型取决于具体需求。通常情况下,如果程序不涉及多语言字符或 Unicode 字符,使用 string 就足够了。如果需要处理多语言字符或 Unicode 字符,可以根据需要选择 u16string、u32string 或 wstring。
这些差异是因为在编码时,根据不同的语言文字需求,而创建的不同的标准,比如UTF-8编码标准,UTF-8 是一种可变长度的 Unicode 编码方式,它可以表示世界上几乎所有的字符,包括 ASCII 字符和其他国际字符。在 C++ 中,string 类使用单字节 char 类型来存储字符,因此可以直接用于处理 UTF-8 编码的字符串。
UTF-8 编码中,ASCII 字符(0-127范围内的字符)只占用一个字节,而其他字符可能占用多个字节,通常是 2 个或 3 个字节。由于 string 类使用单字节 char 类型,它可以直接处理 UTF-8 编码的字符串,无需进行额外的转换或处理。
以下是一个示例,展示了如何使用 string 类来处理 UTF-8 编码的字符串:
#include <iostream>
#include <string>
int main() {
std::string utf8String = u8"Hello, 世界!";
std::cout << utf8String << std::endl;
return 0;
}
在上述示例中,我们使用了 u8 前缀来表示一个 UTF-8 字符串,然后将它存储在 string 类型的变量 utf8String 中,并打印输出(这种方式需要编译器编码格式对应UTF-8,若要按照编译器正常编码格式打印,则无需加u8)。
需要注意的是,尽管 string 类适用于 UTF-8 编码,但如果需要处理更大范围的 Unicode 字符(如 Emoji 表情符号等),可能需要使用 u16string 或 u32string 类,因为它们可以更好地处理多字节的 Unicode 字符。在处理 UTF-8 编码的大部分应用场景中,string 类是非常方便和实用的选择。
2.2 了解string
string类文档介绍
- 字符串是表示字符序列的类
- 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。
- string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信息,请参阅basic_string)。
- string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。
- 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。
总结:
- string是表示字符串的字符串类
- 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
- string在底层实际是:basic_string模板类的别名,
typedef basic_string<char, char_traits, allocator>string;
- 不能操作多字节或者变长字符的序列。
- 在使用string类时,必须包含#include头文件以及using namespace std(或是单独展开std::string)
3.string类的常用接口说明
3.1 string类对象的常见构造
函数名称 | 功能说明 |
---|---|
string(); | 构造空的string类对象,即空字符串 |
string (const string& str); | 拷贝构造函数 |
string (const string& str, size_t pos, size_t len = npos); | 使用另一个 std::string 对象的子串来创建一个新的字符串对象,从给定的位置 pos 复制长度为 len 的子串。 |
string (const char* s); | 用C-string来构造string类对象 |
string (const char* s, size_t n); | 根据部分字符数组来创建一个新的字符串对象 |
string (size_t n, char c); | string类对象中包含n个字符c |
template <class InputIterator> string (InputIterator first, InputIterator last); | 使用迭代器范围 [first, last) 来创建一个新的字符串对象,范围内的字符将被复制到字符串中。 |
示例:
1.默认构造函数
default (1) string();
std::string str; // 创建一个空字符串
2.拷贝构造函数
copy (2) string (const string& str);
std::string str1 = "Hello";
std::string str2 = str1; // 使用复制构造函数创建新的字符串对象
3.子串构造函数
substring (3) string (const string& str, size_t pos, size_t len = npos);
std::string str1 = "Hello, World!";
std::string str2 = str1.substr(0, 5); // 使用子串构造函数创建新的字符串对象,内容为 "Hello"
4.字符串字面值构造函数
from c-string (4) string (const char* s);
std::string str = "Hello, World!"; // 使用字符串字面值初始化字符串
5.部分字符数组构造函数
from sequence (5) string (const char* s, size_t n);
const char* str = "Hello, World!";
std::string new_str(str, 5); // 使用字符数组的前5个字符 "Hello" 来创建新的字符串对象
6.字符重复构造函数
fill (6) string (size_t n, char c);
std::string str(5, 'A'); // 创建一个包含5个字符'A'的字符串 "AAAAA"
7.迭代器范围构造函数
range (7) template <class InputIterator> string (InputIterator first, InputIterator last);
std::string str("Hello, World!");
std::string sub_str(str.begin(), str.begin() + 5); // 使用迭代器范围构造函数创建新的字符串对象,内容为 "Hello"
3.2 string类对象的容量操作
函数名称 | 功能说明 |
---|---|
size | 返回字符串有效字符长度 |
length | 返回字符串有效字符长度 |
max_size | 返回字符串对象能够容纳的最大字符数 |
resize | 用于改变字符串对象的长度 |
capacity | 返回空间总大小 |
reserve | 为字符串预留空间 |
clear | 清空有效字符 |
empty | 检测字符串释放为空串,是返回true,否则返回false |
shrink_to_fit | 要求字符串对象释放多余的内存空间,将容量调整为与字符串长度相同 |
示例:
1.返回字符串有效字符长度
size_type size() const;
string s1("hello world");
//利用有效长度逐个打印字符,并间隔打印
for (size_t i = 0; i < s1.size(); ++i)
{
cout << s1[i] << ' ';
}
2.返回字符串有效字符长度
size_type length() const;
size() 和 length() 是完全等价的,它们都返回字符串对象中字符的数量,没有区别。
这两个函数的作用是相同的,只是命名不同,为了提供更多的编程灵活性和可读性,可以根据个人偏好选择使用 size() 或 length()。
在实际使用中,你可以根据自己的喜好和项目约定,选择使用 size() 或 length() 来获取字符串的长度,两者效果完全相同。一般情况下,size() 更为常用,因为在 C++ 标准库中,很多容器都提供了 size() 成员函数用于获取容器中元素的数量,包括 std::string、std::vector 等。所以,使用 size() 可以保持代码的一致性和易读性。
3.返回字符串对象能够容纳的最大字符数
size_type max_size() const;
std::string str;
std::cout << "Max size: " << str.max_size() << std::endl;
max_size() 函数返回字符串对象在当前系统环境下能够容纳的最大字符数,这个值取决于平台和编译器,并且通常是一个非常大的值。它的含义是字符串对象的理论上限,表示在当前平台和环境下,该类型的字符串对象最多能够容纳的字符数量。
4.用于改变字符串对象的长度
void resize (size_type n);void resize (size_type n, charT c);
第一个版本的 resize() 函数用于改变字符串对象的长度。
resize() 会将字符串的长度调整为 n,如果 n 小于当前长度,则会截断字符串;如果 n 大于当前长度,则会在末尾添加空字符以扩展字符串长度。
第二个版本的 resize() 会将字符串的长度调整为 n,同时用字符 c 来填充新添加的字符(仅当 n 大于当前长度时生效)。
示例:
std::string str = "Hello";
str.resize(10); // 调整字符串长度为 10,现在 str 为 "Hello "
str.resize(15, '-'); // 调整字符串长度为 15,用字符 '-' 填充,现在 str 为 "Hello-----"
5.返回空间总大小
size_type capacity() const;
capacity() 函数返回当前字符串对象的容量,即字符串对象在不重新分配内存的情况下所能容纳的最大字符数。它的值可能小于或等于 max_size(),取决于当前字符串对象的实际内存分配情况。在 C++ 中,std::string 类采用动态分配内存的方式存储字符串,当字符串的长度超过当前容量时,字符串会自动重新分配更大的内存空间。
6.为字符串预留空间
void reserve (size_type n = 0);
在 C++ 的 std::string 类中,reserve() 是一个成员函数,用于请求字符串对象重新分配内存,以便能够容纳至少指定数量的字符。这个函数在字符串需要频繁增长长度时非常有用,它可以避免多次内存分配和拷贝操作,从而提高性能。
std::string str = "Hello";
std::cout << "Capacity before reserve: " << str.capacity() << std::endl; // 输出当前容量
str.reserve(20); // 请求重新分配至少能容纳20个字符的内存空间
std::cout << "Capacity after reserve: " << str.capacity() << std::endl; // 输出重新分配后的容量
在上面的示例中,我们先输出了字符串 str 的当前容量,然后调用 reserve() 函数请求重新分配至少能容纳 20 个字符的内存空间,最后再次输出字符串 str 的容量。请注意,重新分配内存后,容量可能超过请求的 n 值,这是为了避免频繁的内存重新分配。
使用 reserve() 函数可以优化字符串对象的内存使用,特别是在预知字符串会变得较长时,提前分配足够的内存空间,减少动态内存分配和拷贝操作,从而提高程序的性能。但要注意,过度分配内存可能导致资源浪费,应根据实际情况合理选择容量。
7.清空有效字符
void clear();
使用 clear() 函数会将字符串对象的内容清空,即将其长度设置为 0,但并不会释放内存或缩小容量。它会将字符串对象变为空字符串,等效于给字符串赋值一个空字符串。
std::string str = "Hello, World!";
std::cout << "Before clear: " << str << std::endl; // 输出原字符串内容
str.clear(); // 清空字符串内容,使其变为空字符串
std::cout << "After clear: " << str << std::endl; // 输出空字符串
在上面的示例中,我们先输出字符串 str 的内容,然后调用 clear() 函数将其内容清空,最后再次输出字符串 str,此时已经变为空字符串。
clear() 函数在清空字符串内容时非常方便,尤其在需要重新使用同一个字符串对象或者需要重置字符串内容的情况下,可以使用这个函数来快速清空字符串。请注意,clear() 只清空字符串内容,不会释放内存或改变容量。如果需要释放内存或缩小容量,可以使用 shrink_to_fit() 函数。
8.检测字符串释放为空串
bool empty() const;
empty() 函数返回一个 bool 类型的值,如果字符串对象为空,则返回 true,否则返回 false。
std::string str1 = "Hello";
std::string str2;
if (str1.empty()) {
std::cout << "str1 is empty." << std::endl;
} else {
std::cout << "str1 is not empty." << std::endl;
}
if (str2.empty()) {
std::cout << "str2 is empty." << std::endl;
} else {
std::cout << "str2 is not empty." << std::endl;
}
在上面的示例中,我们创建了两个 std::string 对象 str1 和 str2,然后使用 empty() 函数检查它们是否为空。str1 包含字符 “Hello”,所以 str1.empty() 返回 false,而 str2 是一个空字符串,所以 str2.empty() 返回 true。
empty() 函数在实际编程中非常有用,可以用于检查字符串是否为空,从而根据情况进行逻辑判断和处理。
9.释放多余的内存空间
void shrink_to_fit();
该函数用于要求字符串对象释放多余的内存空间,将容量调整为与字符串长度相同。这样可以减少不必要的内存使用。
std::string str = "Hello";
str.reserve(20); // 请求重新分配至少能容纳20个字符的内存空间
// 其他操作...
str.shrink_to_fit(); // 释放多余的内存空间,容量调整为与字符串长度相同
使用 reserve() 和 shrink_to_fit() 函数可以有效地控制字符串对象的内存使用,避免不必要的内存分配和释放。请注意,在字符串需要频繁改变长度时,动态地调整容量可能是一个优化的策略。不过,也要根据实际情况进行合理的内存管理,以避免过度分配内存导致的资源浪费。
注意:
- size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。
- clear()只是将string中有效字符清空,不改变底层空间大小。
- resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
- reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserve不会改变容量大小。
3.3 string类对象的元素访问
函数名称 | 功能说明 |
---|---|
operator[] | 访问字符串中指定位置 pos 处的字符 |
at | 访问字符串中指定位置 pos 处的字符 |
back | 获取字符串的第一个字符 |
front | 获取字符串的最后一个字符 |
1. operator[]:
reference operator[](size_type pos);
const_reference operator[](size_type pos) const;
operator[] 函数用于访问字符串中指定位置 pos 处的字符。返回一个引用,允许您读取或修改该位置上的字符。如果使用 const 修饰符,则表示对字符串进行只读访问。
std::string str = "Hello";
char first_char = str[0]; // 获取字符串的第一个字符 'H'
str[0] = 'h'; // 修改字符串的第一个字符为 'h',现在 str 为 "hello"
2.at():
reference at(size_type pos);
const_reference at(size_type pos) const;
at() 函数与 operator[] 类似,也用于访问字符串中指定位置 pos 处的字符。不同之处在于,at() 函数会检查索引是否有效,如果索引超出字符串范围,则抛出 std::out_of_range 异常。
std::string str = "Hello";
char first_char = str.at(0); // 获取字符串的第一个字符 'H'
str.at(0) = 'h'; // 修改字符串的第一个字符为 'h',现在 str 为 "hello"
3.front():
reference front();
const_reference front() const;
front() 函数用于获取字符串的第一个字符,返回一个引用。如果使用 const 修饰符,则表示对字符串进行只读访问。
std::string str = "Hello";
char first_char = str.front(); // 获取字符串的第一个字符 'H'
4.back():
reference back();
const_reference back() const;
back() 函数用于获取字符串的最后一个字符,返回一个引用。如果使用 const 修饰符,则表示对字符串进行只读访问。
std::string str = "Hello";
char last_char = str.back(); // 获取字符串的最后一个字符 'o'
这些函数提供了多种方式来访问字符串的内容,你可以根据需要选择合适的函数来读取或修改字符串中的字符。请注意,在使用这些函数时要注意索引的有效范围,避免访问越界导致的未定义行为。
3.4 string类对象的Iterators(迭代器)接口
在 C++ 的 std::string 类中,提供了许多 Iterators(迭代器)接口函数,用于遍历和访问字符串中的字符。这些函数允许你以迭代器的方式访问字符串的内容,从而对字符串进行遍历和操作。
以下是 std::string 类的一些 Iterators 接口函数及其介绍和使用方法:
函数名称 | 功能说明 |
---|---|
begin() 和 end() | begin() 函数返回一个指向字符串首字符的迭代器,而 end() 函数返回一个指向字符串末尾后一个字符的迭代器 |
rbegin() 和 rend() | rbegin() 函数返回一个指向字符串最后一个字符的逆向迭代器,而 rend() 函数返回一个指向字符串首字符前一个位置的逆向迭代器 |
cbegin() 和 cend() | cbegin() 函数返回一个指向字符串首字符的 const 迭代器,而 cend() 函数返回一个指向字符串末尾后一个字符的 const 迭代器(末尾后的一个位置)。 |
crbegin() 和 crend() | crbegin() 函数返回一个指向字符串最后一个字符的 const 逆向迭代器,而 crend() 函数返回一个指向字符串首字符前一个位置的 const 逆向迭代器。 |
1.begin() 和 end():
iterator begin();
const_iterator begin() const;
iterator end();
const_iterator end() const;
begin() 函数返回一个指向字符串首字符的迭代器,而 end() 函数返回一个指向字符串末尾后一个字符的迭代器(末尾后的一个位置)。const 修饰符版本的函数用于在 const 对象上获取 const 迭代器。
string s("hello");
string::iterator it = s.begin();
while (it != s.end())
{
cout << *it << " ";
++it;
}
cout << endl;
2.rbegin() 和 rend():
reverse_iterator rbegin();
const_reverse_iterator rbegin() const;
reverse_iterator rend();
const_reverse_iterator rend() const;
rbegin() 函数返回一个指向字符串最后一个字符的逆向迭代器,而 rend() 函数返回一个指向字符串首字符前一个位置的逆向迭代器。const 修饰符版本的函数用于在 const 对象上获取 const 逆向迭代器。
std::string str = "Hello";
for (auto it = str.rbegin(); it != str.rend(); ++it) {
std::cout << *it; // 逆向遍历输出字符串的所有字符 "olleH"
}
3.cbegin() 和 cend():
const_iterator cbegin() const;
const_iterator cend() const;
cbegin() 函数返回一个指向字符串首字符的 const 迭代器,而 cend() 函数返回一个指向字符串末尾后一个字符的 const 迭代器(末尾后的一个位置)。
4.crbegin() 和 crend():
const_reverse_iterator crbegin() const;
const_reverse_iterator crend() const;
crbegin() 函数返回一个指向字符串最后一个字符的 const 逆向迭代器,而 crend() 函数返回一个指向字符串首字符前一个位置的 const 逆向迭代器。
这些 Iterators 接口函数提供了多种方式来遍历字符串的内容,并且支持正向和逆向迭代器。你可以根据需要选择合适的迭代器类型来遍历字符串,其中 begin() 和 end() 是最常用的迭代器接口函数。
范围for和迭代器的关系
范围for循环(Range-based for loop)和迭代器(Iterators)是 C++ 中两种不同的用于遍历容器或可迭代对象的方式。它们在语法和用法上有一些区别,但本质上都是用来迭代访问容器或序列中的元素。
- 范围for循环(Range-based for loop):
范围for循环是 C++11 引入的一种语法糖,用于简化容器或序列的遍历。它的语法形式如下:
for (element_type element : container) {
// 对每个元素执行的操作
}
在这个循环中,container 是一个可迭代对象,可以是标准容器(如 std::vector、std::list、std::map 等)、数组、字符串等等。element 是容器中的每个元素的副本,可以用于访问和操作该元素。范围for循环会自动遍历容器,并在每次迭代时将容器中的元素赋值给 element,直到容器的末尾为止。
示例:
std::string str = "Hello";
for (char c : str) {
std::cout << c << " "; // 输出:H e l l o
}
- 迭代器:
迭代器也可以用于遍历 std::string 中的每个字符,它提供了更灵活的方式来访问字符串的内容,可以自定义遍历方式。std::string 类提供了两种迭代器:iterator 和 const_iterator。
iterator:用于修改 std::string 对象中的字符。
const_iterator:用于只读访问 std::string 对象中的字符。
示例:
std::string str = "Hello";
for (std::string::iterator it = str.begin(); it != str.end(); ++it) {
std::cout << *it << " "; // 输出:H e l l o
}
或者使用 auto 关键字简化迭代器声明:
for (auto it = str.begin(); it != str.end(); ++it) {
std::cout << *it << " "; // 输出:H e l l o
}
需要注意的是,范围for循环和迭代器都可以用来遍历 std::string 中的字符,范围for循环更简洁方便,特别适用于遍历整个字符串;而迭代器提供了更多灵活性,可以用于实现更复杂的遍历和访问方式,比如只遍历部分字符或以逆序遍历字符串。在实际应用中,你可以根据具体需求选择合适的遍历方式。
3.5 string类对象的修改器(Modifiers)
函数名称 | 功能说明 |
---|---|
operator+= | 用于字符串的连接 |
append | 在字符串末尾添加字符或字符序列 |
push_back | 在字符串末尾添加一个字符 |
assign | 将新的内容赋值给字符串对象,从而修改字符串的内容 |
insert | 在指定位置 pos 处插入字符或字符序列 |
erase | 删除指定位置的字符或字符范围 |
replace | 替换指定位置或字符范围的字符 |
swap | 调用该函数的字符串对象与参数表示的另一个字符串对象的内容进行交换 |
pop_back | 删除字符串中的最后一个字符 |
1.operator+=
+= 运算符是字符串的连接操作符,也称为字符串的拼接运算符。使用 += 运算符,您可以将一个字符串连接到另一个字符串的末尾,从而实现字符串的拼接。
+= 运算符的原型如下:
basic_string& operator+=(const basic_string& str);
basic_string& operator+=(const charT* s);
basic_string& operator+=(charT ch);
+= 运算符有三个重载版本,分别用于将另一个字符串、C 风格字符串(以 null 结尾的字符数组)或单个字符连接到当前字符串的末尾。
示例:
std::string str = "Hello";
str += " World"; // 将字符串 " World" 连接到 str 的末尾,现在 str 变为 "Hello World"
str += '!'; // 将字符 '!' 连接到 str 的末尾,现在 str 变为 "Hello World!"
在上述示例中,我们通过使用 += 运算符将字符串 " World" 和字符 ‘!’ 连接到字符串 str 的末尾,实现了字符串的拼接。
需要注意的是,+= 运算符会直接修改调用它的字符串对象,将指定的内容连接到字符串的末尾。如果你希望连接两个字符串而不修改原始字符串,可以使用 + 运算符来创建一个新的字符串,而不会修改原有的字符串对象。例如:
std::string str1 = "Hello";
std::string str2 = " World";
std::string combined = str1 + str2; // 创建一个新的字符串,不修改原有字符串
使用 += 运算符或 + 运算符,取决于你希望修改原有字符串还是创建新的字符串,或者根据具体的需求来选择适合的方法。
2.append
append() 函数用于在字符串的末尾添加字符或字符序列,从而实现字符串的拼接。
append() 函数有多个重载形式,提供了不同的方式来添加内容到字符串的末尾。以下是 std::string 类中 append() 函数的几种重载形式及其介绍和使用方法:
basic_string& append(const charT* s);
这个重载函数将一个 C 风格字符串(以 null 结尾的字符数组)添加到字符串的末尾。
std::string str = "Hello";
str.append(" World"); // 在末尾添加字符串 " World",现在 str 为 "Hello World"
basic_string& append(const charT* s, size_type n);
这个重载函数将一个指定长度的字符数组添加到字符串的末尾。
std::string str = "Hello";
str.append(" World", 5); // 在末尾添加字符数组 " Worl"(前5个字符),现在 str 为 "Hello Worl"
basic_string& append(const basic_string& str);
这个重载函数将另一个 std::string 对象的内容添加到当前字符串的末尾。
std::string str1 = "Hello";
std::string str2 = " World";
str1.append(str2); // 将 str2 的内容添加到 str1 的末尾,现在 str1 为 "Hello World"
basic_string& append(const basic_string& str, size_type pos, size_type n);
这个重载函数将另一个 std::string 对象从指定位置 pos 处开始的 n 个字符添加到当前字符串的末尾。
std::string str1 = "Hello";
std::string str2 = "World";
str1.append(str2, 1, 3); // 将 str2 从位置1(包含)开始的3个字符 "orl" 添加到 str1 的末尾,现在 str1 为 "Helloorl"
basic_string& append(size_type n, charT ch);
这个重载函数将重复 n 次的字符 ch 添加到字符串的末尾。
std::string str = "Hello";
str.append(3, '!'); // 在末尾添加3个字符 '!',现在 str 为 "Hello!!!"
这些 append() 函数提供了多种方式来将内容添加到字符串的末尾,从而实现字符串的拼接。
3.push_back
void push_back(charT ch);
使用 push_back() 函数可以将一个字符 ch 添加到字符串的末尾。
示例:
std::string str = "Hello";
str.push_back('!'); // 在末尾添加字符 '!',现在 str 变为 "Hello!"
在上述示例中,我们通过使用 push_back() 函数在字符串 str 的末尾添加了字符 ‘!’,从而实现了在字符串中添加一个字符的操作。
需要注意的是,push_back() 函数会直接修改调用它的字符串对象,在字符串的末尾添加指定的字符。在使用 push_back() 之前,应该确保字符串的长度仍然在合理的范围内,避免发生字符串的溢出。在实际使用中,务必注意处理字符串长度和内存的限制。另外,C++11 之后的版本还引入了字符串拼接操作符 +=,可以使用 += 运算符来在字符串的末尾添加字符,更加简洁方便。
4.assign
assign() 函数用于将新的内容赋值给字符串对象,从而修改字符串的内容。
assign() 函数有多个重载形式,提供了不同的方式来赋值新的内容给字符串。以下是 std::string 类中 assign() 函数的几种重载形式及其介绍和使用方法:
basic_string& assign(const charT* s);
这个重载函数将一个 C 风格字符串(以 null 结尾的字符数组)赋值给字符串对象。
std::string str;
str.assign("Hello"); // 将 C 风格字符串 "Hello" 赋值给字符串对象 str
basic_string& assign(const charT* s, size_type n);
这个重载函数将一个指定长度的字符数组赋值给字符串对象。
std::string str;
str.assign("Hello", 3); // 将字符数组 "Hel"(前三个字符)赋值给字符串对象 str
basic_string& assign(const basic_string& str);
这个重载函数将另一个 std::string 对象的内容赋值给当前字符串对象。
std::string str1 = "Hello";
std::string str2;
str2.assign(str1); // 将 str1 的内容赋值给 str2
basic_string& assign(const basic_string& str, size_type pos, size_type n);
这个重载函数将另一个 std::string 对象从指定位置 pos 处开始的 n 个字符赋值给当前字符串对象。
std::string str1 = "Hello";
std::string str2;
str2.assign(str1, 1, 3); // 将 str1 从位置1(包含)开始的3个字符 "ell" 赋值给 str2
basic_string& assign(size_type n, charT ch);
这个重载函数将重复 n 次的字符 ch 赋值给字符串对象。
std::string str;
str.assign(5, 'x'); // 将字符 'x' 重复5次赋值给字符串对象 str,结果为 "xxxxx"
这些 assign() 函数提供了多种方式来将新的内容赋值给字符串对象,从而修改字符串的内容。
5.insert
insert() 函数用于在指定位置插入字符或字符序列,从而改变字符串的内容。
insert() 函数有多个重载形式,提供了不同的方式来在字符串中插入字符或字符序列。以下是 std::string 类中 insert() 函数的几种重载形式及其介绍和使用方法:
basic_string& insert(size_type pos, const charT* s);
这个重载函数在字符串中的指定位置 pos 处插入一个 C 风格字符串(以 null 结尾的字符数组)。
std::string str = "Hello";
str.insert(2, "xx"); // 在位置2处插入字符串 "xx",现在 str 变为 "Hexxllo"
basic_string& insert(size_type pos, const charT* s, size_type n);
这个重载函数在字符串中的指定位置 pos 处插入一个指定长度的字符数组。
std::string str = "Hello";
str.insert(3, "xx", 1); // 在位置3处插入字符数组 "x"(前1个字符),现在 str 变为 "Helxlo"
basic_string& insert(size_type pos, const basic_string& str);
这个重载函数在字符串中的指定位置 pos 处插入另一个 std::string 对象的内容。
std::string str1 = "Hello";
std::string str2 = " World";
str1.insert(5, str2); // 在位置5处插入 str2 的内容,现在 str1 变为 "Hello World"
basic_string& insert(size_type pos, const basic_string& str, size_type subpos, size_type sublen);
这个重载函数在字符串中的指定位置 pos 处插入另一个 std::string 对象的子字符串,从 str 的 subpos 处开始,长度为 sublen。
std::string str1 = "Hello";
std::string str2 = "World";
str1.insert(5, str2, 0, 3); // 在位置5处插入 str2 的子字符串 "Wor",现在 str1 变为 "HelloWor"
basic_string& insert(size_type pos, size_type n, charT ch);
这个重载函数在字符串中的指定位置 pos 处插入重复 n 次的字符 ch。
std::string str = "Hello";
str.insert(2, 3, 'x'); // 在位置2处插入3个字符 'x',现在 str 变为 "Hexxxello"
这些 insert() 函数提供了多种方式来在字符串中插入字符或字符序列,从而实现字符串内容的修改。
6.erase
erase() 函数用于从字符串中删除指定位置的字符或字符序列,从而修改字符串的内容。
erase() 函数有多个重载形式,提供了不同的方式来删除字符串中的字符或字符序列。以下是 std::string 类中 erase() 函数的几种重载形式及其介绍和使用方法:
basic_string& erase(size_type pos = 0, size_type n = npos);
这个重载函数删除从指定位置 pos 开始的 n 个字符(默认情况下,删除从 pos 开始的所有字符)。
std::string str = "Hello World";
str.erase(5); // 删除从位置5(包含)开始的所有字符,现在 str 变为 "Hello"
str.erase(0, 3); // 删除从位置0(包含)开始的3个字符,现在 str 变为 "lo"
iterator erase(const_iterator position);
这个重载函数删除指定位置 position 处的字符,并返回一个指向删除后的下一个字符的迭代器。
std::string str = "Hello";
auto it = str.erase(str.begin() + 1); // 删除位置1处的字符 'e',现在 str 变为 "Hllo",it 指向 'l'
iterator erase(const_iterator first, const_iterator last);
这个重载函数删除从 first 到 last-1之间的字符,并返回一个指向删除后的下一个字符的迭代器。
std::string str = "Hello World";
auto first = str.begin() + 6; // 指向字符 'W'
auto last = str.begin() + 11; // 指向字符 '\0'
auto it = str.erase(first, last); // 删除字符 'W' 到 '\0'(不包括\0),现在 str 变为 "Hello ",it 指向 '\0'
这些 erase() 函数提供了多种方式来删除字符串中的字符或字符序列,从而实现字符串内容的修改。
7.replace
replace()函数用于将字符串中的一部分内容替换为新的子串。
replace() 函数有多个重载形式,提供了不同的方式来替换字符串中的一部分内容。以下是 std::string 类中 replace() 函数的几种重载形式及其描述和使用方法:
basic_string& replace(size_type pos, size_type count, const charT* s);
这个重载函数从字符串的位置 pos 开始,用指定的 C 风格字符串(以 null 结尾的字符数组)替换 count 个字符。
std::string str = "Hello, World!";
str.replace(7, 5, "Universe"); // 从位置 7 开始,用 "Universe" 替换 5 个字符,现在 str 变为 "Hello, Universe!"
basic_string& replace(size_type pos, size_type count, const charT* s, size_type n);
这个重载函数从字符串的位置 pos 开始,用指定长度的字符数组中的前 n 个字符替换 count 个字符。
std::string str = "Hello, World!";
str.replace(7, 5, "Earth", 3); // 从位置 7 开始,用 "Ear"("Earth" 的前 3 个字符)替换 5 个字符,现在 str 变为 "Hello, Ear, World!"
basic_string& replace(size_type pos, size_type count, const basic_string& str);
这个重载函数从字符串的位置 pos 开始,用另一个 std::string 对象的内容替换 count 个字符。
std::string str1 = "Hello, World!";
std::string str2 = "Universe";
str1.replace(7, 5, str2); // 从位置 7 开始,用 str2 的内容 "Universe" 替换 5 个字符,现在 str1 变为 "Hello, Universe!"
basic_string& replace(size_type pos, size_type count, const basic_string& str, size_type pos2, size_type count2);
这个重载函数从字符串的位置 pos 开始,用另一个 std::string 对象 str 的从 pos2 处开始的 count2 个字符替换 count 个字符。
std::string str1 = "Hello, World!";
std::string str2 = "Universe";
str1.replace(7, 5, str2, 0, 3); // 从位置 7 开始,用 str2 的子串 "Uni"(从位置 0 开始的 3 个字符)替换 5 个字符,现在 str1 变为 "Hello, Uni, World!"
basic_string& replace(iterator first, iterator last, const charT* s);
这个重载函数用指定的 C 风格字符串(以 null 结尾的字符数组)替换从 first 到 last-1 之间的字符。
std::string str = "Hello, World!";
auto first = str.begin() + 7; // 指向字符 'W'
auto last = str.begin() + 12; // 指向字符 '!'
str.replace(first, last, "Universe"); // 用 "Universe" 替换 'W' 到 '!'(不包括!),现在 str 变为 "Hello, Universe!"
basic_string& replace(iterator first, iterator last, const basic_string& str);
这个重载函数用另一个 std::string 对象 str 的内容替换从 first 到 last-1 之间的字符。
std::string str1 = "Hello, World!";
std::string str2 = "Universe";
auto first = str1.begin() + 7; // 指向字符 'W'
auto last = str1.begin() + 12; // 指向字符 '!'
str1.replace(first, last, str2); // 用 str2 的内容 "Universe" 替换 'W' 到 '!'(不包含 '!'),现在 str1 变为 "Hello, Universe!"
这些 replace() 函数提供了多种方式来替换字符串中的一部分内容,从而实现字符串内容的修改。
8.swap
swap() 函数没有参数,它将调用它的字符串对象与另一个字符串对象进行内容交换。
std::string str1 = "Hello";
std::string str2 = "World";
str1.swap(str2); // 将 str1 和 str2 的内容交换,现在 str1 变为 "World",str2 变为 "Hello"
在上述示例中,通过调用 str1.swap(str2),我们将 str1 和 str2 的内容进行了交换,str1 变为 “World”,而 str2 变为 “Hello”。
这个函数可以在交换两个字符串的内容时非常有用,而不需要对字符逐个交换或使用临时变量来完成交换。由于 swap() 是一个成员函数,所以可以通过调用它来直接对字符串对象进行交换操作,使得代码更简洁和高效。
9.pop_back
pop_back() 函数没有参数,它只需调用它的字符串对象,就会将最后一个字符从字符串中删除。
std::string str = "Hello";
str.pop_back(); // 删除最后一个字符,现在 str 变为 "Hell"
在上述示例中,通过调用 str.pop_back(),我们将字符串 str 的最后一个字符 ‘o’ 删除,从而得到新的字符串 “Hell”。
需要注意的是,使用 pop_back() 之前,应该确保字符串的长度不为零,否则会引发未定义的行为。在调用 pop_back() 之前,通常需要检查字符串是否为空。
3.6 string类对象的操作函数(operations)
1.c_str():
const char* c_str() const;
这个函数返回一个指向以 null 结尾的字符数组(C 字符串)的指针,表示当前字符串的内容。
示例:
std::string str = "Hello";
const char* cstr = str.c_str(); // cstr 指向 "Hello" 的 C 字符串形式
2.data():
const char* data() const;
这个函数返回一个指向字符数组的指针,表示当前字符串的内容。与 c_str() 类似,但 data() 不一定以 null 结尾。
示例:
std::string str = "Hello";
const char* strData = str.data(); // strData 指向 "Hello" 的字符数组形式
3.get_allocator():
allocator_type get_allocator() const noexcept;
这个函数返回当前字符串使用的分配器的副本。
示例:
std::string str = "Hello";
std::allocator<char> alloc = str.get_allocator(); // 获取 str 的分配器副本
4.copy():
size_type copy(charT* dest, size_type count, size_type pos = 0) const;
这个函数从当前字符串中复制 count 个字符到指定位置 pos 开始的字符数组 dest。
示例:
std::string str = "Hello";
char buffer[6];
str.copy(buffer, 5); // 将 str 的前 5 个字符复制到 buffer 中,buffer 现在为 "Hello\0"(带有 null 终止符)
5.find()、rfind()、find_first_of()、find_last_of()、find_first_not_of()、find_last_not_of():
这些函数用于在当前字符串中搜索指定的字符或子字符串,并返回找到的第一个匹配位置或位置偏移。
示例:
std::string str = "Hello, World!";
size_t pos = str.find("World"); // 在 str 中找到子字符串 "World",返回位置 7
size_t lastPos = str.rfind("l"); // 从字符串末尾开始找到字符 'l',返回位置 9
size_t foundPos = str.find_first_of(",!"); // 在 str 中找到第一个出现的 ',' 或 '!',返回位置 5
6. substr():
basic_string substr(size_type pos = 0, size_type count = npos) const;
这个函数返回一个新的字符串,包含从位置 pos 开始的 count 个字符的副本。
示例:
void DealUrl(const string& url)
{
size_t pos1 = url.find("://");
if (pos1 == string::npos)
{
cout << "非法url" << endl;
return;
}
string protocol = url.substr(0, pos1);
cout << protocol << endl;
size_t pos2 = url.find('/', pos1 + 3);
if (pos2 == string::npos)
{
cout << "非法url" << endl;
return;
}
string domain = url.substr(pos1+3, pos2-pos1-3);
cout << domain << endl;
string uri = url.substr(pos2 + 1);
cout << uri << endl << endl;
}
int main()
{
string url1 = "https://cplusplus.com/reference/string/string/";
string url2 = "https://image.baidu.com/search/detail?ct=503316480&z=0&ipn=d&word=ascall&step_word=&hs=0&pn=0&spn=0&di=7108135681917976577&pi=0&rn=1&tn=baiduimagedetail&is=0%2C0&istype=0&ie=utf-8&oe=utf-8&in=&cl=2&lm=-1&st=undefined&cs=2613959014%2C543572025&os=2740573600%2C1059518451&simid=2613959014%2C543572025&adpicid=0&lpn=0&ln=179&fr=&fmq=1660115697093_R&fm=&ic=undefined&s=undefined&hd=undefined&latest=undefined©right=undefined&se=&sme=&tab=0&width=undefined&height=undefined&face=undefined&ist=&jit=&cg=&bdtype=0&oriquery=&objurl=https%3A%2F%2Fgimg2.baidu.com%2Fimage_search%2Fsrc%3Dhttp%3A%2F%2Fimg.php.cn%2Fupload%2Fimage%2F147%2F157%2F796%2F1593765739620093.png%26refer%3Dhttp%3A%2F%2Fimg.php.cn%26app%3D2002%26size%3Df9999%2C10000%26q%3Da80%26n%3D0%26g%3D0n%26fmt%3Dauto%3Fsec%3D1662707704%26t%3Da68cb238bbb3f99d0554098c785d526e&fromurl=ippr_z2C%24qAzdH3FAzdH3Fooo_z%26e3Brir_z%26e3BvgAzdH3FuwqAzdH3F9c9amd_z%26e3Bip4s&gsm=1&rpstart=0&rpnum=0&islist=&querylist=&nojc=undefined&dyTabStr=MCwzLDIsNCw2LDEsNSw3LDgsOQ%3D%3D";
string url3 = "ftp://ftp.cs.umd.edu/pub/skipLists/skiplists.pdf";
DealUrl(url1);
DealUrl(url2);
DealUrl(url3);
return 0;
}
7.compare():
int compare(const basic_string& str) const noexcept;
这个函数用于比较当前字符串与另一个 std::string 对象 str 的大小关系。
示例:
std::string str1 = "Hello";
std::string str2 = "World";
int result = str1.compare(str2); // 返回一个整数,表示 str1 和 str2 的大小关系(类似于字符串比较的结果)
这些成员函数提供了丰富的功能,用于处理字符串的查找、比较、复制、截取等操作。
3.7 string类对象的非成员函数和npos
1.operator+(重载运算符+):
template <class CharT, class Traits, class Allocator>
basic_string<CharT, Traits, Allocator> operator+(
const basic_string<CharT, Traits, Allocator>& lhs,
const basic_string<CharT, Traits, Allocator>& rhs);
这个函数模板用于将两个 std::string 对象进行字符串连接,返回一个新的 std::string 对象,包含两个原始字符串的内容。
示例:
std::string str1 = "Hello";
std::string str2 = " World";
std::string result = str1 + str2; // 返回 "Hello World"
2.关系运算符(Relational operators):
template <class CharT, class Traits, class Allocator>
bool operator==(
const basic_string<CharT, Traits, Allocator>& lhs,
const basic_string<CharT, Traits, Allocator>& rhs);
template <class CharT, class Traits, class Allocator>
bool operator!=(
const basic_string<CharT, Traits, Allocator>& lhs,
const basic_string<CharT, Traits, Allocator>& rhs);
template <class CharT, class Traits, class Allocator>
bool operator<(
const basic_string<CharT, Traits, Allocator>& lhs,
const basic_string<CharT, Traits, Allocator>& rhs);
template <class CharT, class Traits, class Allocator>
bool operator<=(
const basic_string<CharT, Traits, Allocator>& lhs,
const basic_string<CharT, Traits, Allocator>& rhs);
template <class CharT, class Traits, class Allocator>
bool operator>(
const basic_string<CharT, Traits, Allocator>& lhs,
const basic_string<CharT, Traits, Allocator>& rhs);
template <class CharT, class Traits, class Allocator>
bool operator>=(
const basic_string<CharT, Traits, Allocator>& lhs,
const basic_string<CharT, Traits, Allocator>& rhs);
这些函数模板用于对 std::string 对象进行大小比较。可以通过这些运算符对两个字符串进行逐个字符的比较,从而确定它们的大小关系。
示例:
std::string str1 = "Hello";
std::string str2 = "World";
bool isEqual = (str1 == str2); // 返回 false,因为 str1 不等于 str2
bool isGreater = (str1 > str2); // 返回 true,因为 str1 大于 str2
3.swap():
template <class CharT, class Traits, class Allocator>
void swap(
basic_string<CharT, Traits, Allocator>& lhs,
basic_string<CharT, Traits, Allocator>& rhs);
这个函数模板用于交换两个 std::string 对象的内容,实现两个字符串的快速交换。
示例:
std::string str1 = "Hello";
std::string str2 = "World";
swap(str1, str2); // 将 str1 和 str2 的内容交换,现在 str1 变为 "World",str2 变为 "Hello"
4.operator>> 和 operator<<(重载运算符>>和<<):
template <class CharT, class Traits, class Allocator>
std::basic_istream<CharT, Traits>& operator>>(
std::basic_istream<CharT, Traits>& is, basic_string<CharT, Traits, Allocator>& str);
template <class CharT, class Traits, class Allocator>
std::basic_ostream<CharT, Traits>& operator<<(
std::basic_ostream<CharT, Traits>& os, const basic_string<CharT, Traits, Allocator>& str);
这些函数模板用于在输入输出流中读取或输出字符串内容。operator>> 用于从输入流中提取字符串,而 operator<< 用于将字符串插入到输出流中。
示例:
std::string str;
std::cout << "请输入一个字符串:";
std::cin >> str; // 从标准输入流中读取字符串
std::cout << "您输入的字符串是:" << str << std::endl; // 将字符串输出到标准输出流
5.getline():
template <class CharT, class Traits, class Allocator>
std::basic_istream<CharT, Traits>& getline(
std::basic_istream<CharT, Traits>& is, basic_string<CharT, Traits, Allocator>& str,
CharT delim);
这个函数模板用于从输入流中读取一行内容,并将其存储到字符串 str 中,直到遇到指定的分隔符 delim。
示例:
std::string str;
std::cout << "请输入一行文本:";
std::getline(std::cin, str); // 从标准输入流中读取一行文本并存储到 str 中
std::cout << "您输入的文本是:" << str << std::endl;
这些函数和操作符提供了丰富的功能,可以方便地对字符串进行连接、比较、输入输出等操作。
npos
npos 是 std::string 类中的一个静态常量成员,用于表示无效或未找到的位置。它是一个特殊的 std::string::size_type 类型的常量,通常被定义为 std::string::npos,其值在不同的编译器和实现中可能不同,但通常被设为 -1 或一个非常大的值,用于表示在字符串中未找到指定的子串或字符。
npos 主要用于字符串查找操作,比如在使用 find()、rfind()、find_first_of()、find_last_of()、find_first_not_of()、find_last_not_of() 等成员函数时,当查找失败或没有找到指定的子串或字符时,这些函数通常会返回 std::string::npos 来表示无效的位置。
示例:
std::string str = "Hello, World!";
std::string::size_type pos1 = str.find("Universe"); // 在 str 中找不到 "Universe",返回 std::string::npos
std::string::size_type pos2 = str.find('X'); // 在 str 中找不到字符 'X',返回 std::string::npos
std::string::size_type pos3 = str.find("World"); // 在 str 中找到 "World",返回 "World" 在 str 中的位置
在上述示例中,当在字符串 str 中找不到 “Universe” 或字符 ‘X’ 时,find() 函数会返回 std::string::npos 表示查找失败。而当找到 “World” 时,find() 函数会返回 “World” 在字符串中的位置。
注意:npos 的值是一个非常大的无符号整数,因此在比较 std::string::size_type 类型的值时,应使用无符号类型的比较方式,避免可能出现的错误。比如使用 pos != std::string::npos 来判断是否找到了指定的子串或字符。
4.vs和g++下string结构的说明
注意:下述结构是在32位平台下进行验证,32位平台下指针占4个字节。
4.1 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个字节。
4.2 g++下string的结构
g++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指针将来指向一块堆空间,内部包含了如下字段:
空间总大小
字符串有效长度
引用计数
指向堆空间的指针,用来存储字符串。
4.3 vs和g++的string扩容机制
在 C++ 标准中,并未规定 std::string 的扩容机制,因此不同的编译器可能有不同的实现方式。但是,通常情况下,std::string 类的扩容机制都会遵循以下一般原则:
- 初始容量:当创建一个空的 std::string 对象时,编译器会为其分配一些初始的内存空间。这个初始容量可以是一个较小的值,例如 15 或 31,具体取决于编译器的实现。
- 扩容策略:当需要向 std::string 中添加字符时,如果当前容量不足,编译器会自动触发扩容操作。扩容的方式可能是重新分配更大的内存块,并将原有的内容拷贝到新的内存中。
- 扩容大小:在扩容时,编译器通常会增加当前容量的一定倍数,以避免频繁的扩容操作。这样做可以提高字符串的插入效率,但也会导致有些内存可能浪费。
比如下面这段代码在Windows vs2022 的环境下和Linux g++环境下
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s;
size_t sz = s.capacity();
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 << endl;
}
}
return 0;
}
vs2022
g++
结语
有兴趣的小伙伴可以关注作者,如果觉得内容不错,请给个一键三连吧,蟹蟹你哟!!!
制作不易,如有不正之处敬请指出
感谢大家的来访,UU们的观看是我坚持下去的动力
在时间的催化剂下,让我们彼此都成为更优秀的人吧!!!