🌈个人主页:秋风起,再归来~****
🔥系列专栏:C++从入门到起飞****
🔖克心守己,律己则安
目录
[1. 为什么学习string类?](#1. 为什么学习string类?)
[1.1 C语言中的字符串](#1.1 C语言中的字符串)
[1.2 两个面试题(先不做讲解)](#1.2 两个面试题(先不做讲解))
[2. 标准库中的string类](#2. 标准库中的string类)
[2.1 string类(了解)](#2.1 string类(了解))
[2.2 string类常用的构造函数](#2.2 string类常用的构造函数)
[(1) 空字符串构造函数(默认构造函数)(重要)](#(1) 空字符串构造函数(默认构造函数)(重要))
[(3) substring(子串,子链) 构造函数](#(3) substring(子串,子链) 构造函数)
[(4) 常量字符串构造函数(重要)](#(4) 常量字符串构造函数(重要))
[(5) 拷贝字符串前n个构造函数](#(5) 拷贝字符串前n个构造函数)
[(6) 填充构造函数](#(6) 填充构造函数)
[2.3 string类可用的三种遍历方式](#2.3 string类可用的三种遍历方式)
[2.4 string类的容量操作](#2.4 string类的容量操作)
[2、resize (重点)](#2、resize (重点))
(需要用到直接查文档即可(链接都在下面),这里标注了一些重要的常用的接口)
[2.5 string类的修改操作](#2.5 string类的修改操作)
[2.6 string类的查找操作](#2.6 string类的查找操作)
[2.7 string类的非成员函数](#2.7 string类的非成员函数)
[3. auto关键字](#3. auto关键字)
[4. vs和g++下string结构的说明](#4. vs和g++下string结构的说明)
[4.1 vs下string的结构](#4.1 vs下string的结构)
[4.2 g++下string的结构](#4.2 g++下string的结构)
[5. 完结散花](#5. 完结散花)
1. 为什么学习string类?
1.1 C语言中的字符串
>C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列 的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。
1.2 两个面试题(先不做讲解)
>在OJ中,有关字符串的题目基本以string类的形式出现,而且在常规工作中,为了简单、方便、 快捷,基本都使用string类,很少有人去使用C库中的字符串操作函数。
2. 标准库中的string类
2.1 string类(了解)
>在使用string类时,必须包含#include<string>头文件以及using namespace std;
2.2 string类常用的构造函数
(1) 空字符串构造函数(默认构造函数)(重要)
string();
//default (1)
//string();
string s1;
cout << "s1;"<< s1<<"****" << endl;
>string类当中重载了流插入和流提取函数,所以我们可以直接用
(2)拷贝构造函数(重要)
string(const string & str);
>构造 str 的副本。
//copy(2)
//string(const string & str);
string s2 = "hello world!";//==string s2("hello world!") 下面会讲这个构造函数
cout << "s2:" << s2 << endl;
string s3 = s2;//==string s3(s2)
cout << "s3:" << s3 << endl;
(3) substring(子串,子链) 构造函数
string(const string & str, size_t pos, size_t len = npos);
>复制 str 中从字符位置 pos 开始并跨越 len 字符的部分(如果任一 字符串str 太短或 len 为string::npos,则复制到 str 末尾的部分)。
//substring(3)
//string(const string & str, size_t pos, size_t len = npos);
string s1 = "hello world!";
cout << "s1:" << s1 << endl;
string s2(s1, 6, 5);
cout << "s2:" << s2 << endl;
>如果拷贝的字符串太短(len太长),则从pos位置拷贝到字符串结尾就停止拷贝!
string s1 = "hello world!";
cout << "s1:" << s1 << endl;
string s2(s1, 6, 100);//向后拷贝100个字符,但字符串s1不够大,只拷贝到结尾就停止
cout << "s2:" << s2 << endl;
>如果我们没有给len赋值,则len使用缺省值npos
>npos是string类当中的一个静态成员变量。
string s1 = "hello world!";
cout << "s1:" << s1 << endl;
string s2(s1, 6);//拷贝到结尾才停止
cout << "s2:" << s2 << endl;
(4) 常量字符串构造函数(重要)
string(const char* s);
>复制 s 指向的以 null 结尾的字符序列(C 字符串)。
//from c - string(4)
//string(const char* s);
const char* s = "hello world!";
string s1(s);//==string s1("hello world!")==string s1=s
cout << "s1:" << s1 << endl;
(5) 拷贝字符串前n个构造函数
string(const char* s, size_t n);
>从 s 指向的字符数组中复制前 n 个字符。
//from buffer(5)
//string(const char* s, size_t n)
const char* s = "hello world!";
string s1(s,5);
cout << "s1:" << s1 << endl;
>如果指定的大小超过原字符串的大小,则将原字符串全部拷贝,并在编译器上打印提示,而程序并不会终止!
(6) 填充构造函数
string(size_t n, char c);
>用字符 c 的 n 个连续副本填充字符串。
//fill(6)
//string(size_t n, char c);
string s1(5, 'x');
cout << s1<<endl;
2.3 string类可用的三种遍历方式
(1)下标访问遍历
>在 string类里面重载了一个公共的成员函数operator[] ,它的返回值是当前pos位置的引用,当没有const修饰时,我们可以对当前字符进行读和写的操作。但当有const修饰时,当前下标的字符是只读的!
>对象调用size和length 都返回该对象的字符串长度,但要注意的是,该长度不包含字符串中的'\0'! size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。
string s1 = "hello world";
//1、下标访问遍历
for (int i = 0; i < s1.size() ;i++)
{
cout << s1[i]<<" ";
}
cout << endl;
for (int i = 0; i < s1.size(); i++)
{
s1[i] += 2;//进行写的操作
cout << s1[i] << " ";
}
cout << endl;
(2)迭代器遍历(含4种迭代器用法详解)
>这里只是简单的讲一下迭代器的一小部分用法,在后续的文章会详细讲解迭代器的!
//迭代器遍历
string s2 = "hello world!";
string::iterator it = s2.begin();
> 我们先来看一下上面的一段代码,我们先突破类域指定迭代器iterator 定义了一个变量it,我们目前可以认为s2调用begin返回了s2的起始位置,也就是第一个字符的位置,然后it接受到了这个位置。我们会发现it是一个很像指针的东西 ,但是!但是!但是!it并不一定是指针!后面,我们还会对it进行解引用和++的操作,但是这些运算符可能都是重载过的!然而,我们对它表层就可以理解为指针,因为它的用法和指针非常相似(这里可能就是原始指针)!
>注意:end返回的是字符串的最后一个位置'\0'!
while (it!=s2.end())
{
cout << *it << " ";
it++;
}
cout << endl;
>当然,我们也可以用迭代器对s2进行写的操作,毕竟它的用法和指针几乎一样!
>当然,如果我们只想对s2只读不写的话,我们也可以用cbegin和cend(c就是const的意思)
>字符串本身是const,不能修改!
//迭代器遍历
const string s2 = "hello world!";
string::const_iterator it = s2.cbegin();
while (it != s2.cend())
{
//*it += 2;报错:表达式是不可修改的左值
cout << *it << " ";
it++;
}
>字符串本身不是const,也不能修改!
//迭代器遍历
string s2 = "hello world!";
string::const_iterator it = s2.cbegin();
while (it!=s2.cend())
{
//*it += 2;报错:表达式是不可修改的左值
cout << *it << " ";
it++;
}
cout << endl;
> 这里再介绍一种反向迭代器
//反向迭代器遍历
string s2 = "hello world!";
string::reverse_iterator rit = s2.rbegin();
while (rit != s2.rend())
{
cout << *rit << " ";
rit++;
}
cout << endl;
>注意:rbegin指向的是字符串倒数第一个有效字符(不是'\0'!) ,rend指向的是字符串的第一个字符的前一个位置!rit任然是++,而不是--(这里可以可以肯定的是++运算符一定被重载了)!
>还有最后这一种迭代器,其实就是常量反向迭代器,理解了前面讲的内容,这部分一看就懂了,这里就不赘述了!
(3)范围for遍历
>对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此 C++11中引入了基于范围的for循环。for循环后的括号由冒号" :"分为两部分:第一部分是范围 内用于迭代的变量 ,第二部分则表示被迭代的范围 ,自动迭代,自动取数据,自动判断结束。
>范围for可以作用到数组和容器对象上进行遍历
>范围for的底层很简单,容器遍历实际就是替换为迭代器,这个从汇编层也可以看到。
//范围for遍历
string s2 = "hello world!";
for (auto s : s2)
{
cout << s << " ";
}
cout << endl;
>我们先看到上面的一段代码,其中关键字auto(下文有专门针对auto的讲解,这里知道一些用法就没问题了)先定义了一个变量s,它会自动提取到字符串s2中的每一个字符并识别它的类型从而进行匹配,而范围for遍历会自动遍历字符串,并输出每一个字符。这样看起来范围for非常牛逼,然而它的底层走的还是迭代器的原理!
>不过,当我们不用访问字符串,只是单纯的遍历时,范围for用起来还是非常爽的!
>注意:这里还有一点小坑,我们来看下面一段代码~
//范围for遍历
string s2 = "hello world!";
for (auto s : s2)
{
s += 2;
cout << s << " ";
}
cout << endl;
for (auto s : s2)
{
cout << s << " ";
}
cout << endl;
>通过输出结果,我们可以发现,我们在第一次遍历时,对s2进行了修改的操作,但是在第二次遍历时,s2却没有改变 ! 所以,我们可以得出结论s只是s2中字符的拷贝 ,虽然它底层走的是迭代器的原理,但还是略有不同的 。不过,我们只要在auto后面加上&符号也可以修改!
2.4 string类的容量操作
>size和length在前文已经讲过了,这里便不再赘述!
1、max_size
2、resize (重点)
>将有效字符的个数该成n个,多出的空间用字符c填充
>resize(size_t n) 与 resize(size_t n, char c) 都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时 :resize(n)用0来填充多出的元素空间 ,resize(size_t n, char c)用字符c来填充多出的元素空间 。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小(capacity)不变。
string s1="xxxxxxxxxxxxx";
cout << "总容量大小(capacity):" << s1.capacity() << endl;
cout << "有效字符个数 (size):" << s1.size() << endl;
cout << "s1:" << s1 << endl;
cout<<"修改后--------------------------"<<endl;
s1.resize(5);//减小的是有效字符的个数,容量的大小不变
cout << "总容量大小(capacity):" << s1.capacity() << endl;
cout << "有效字符个数 (size):" << s1.size() << endl;
cout << "s1:" << s1 << endl;
string s1="xxxxxxxxxxxxx";
cout << "总容量大小(capacity):" << s1.capacity() << endl;
cout << "有效字符个数 (size):" << s1.size() << endl;
cout << "s1:" << s1 << endl;
cout<<"修改后--------------------------"<<endl;
s1.resize(30,'y');//增加有效字符的个数,并用'y'填充,且容量的大小增大
cout << "总容量大小(capacity):" << s1.capacity() << endl;
cout << "有效字符个数 (size):" << s1.size() << endl;
cout << "s1:" << s1 << endl;
3、reserve(重要)
>reserve(size_t res_arg=0):为string预留空间 ,不改变有效元素个数 ,当reserve的参数小于string的底层空间总大小时 ,reserver不会改变容量大小。
1、预留空间大于有效字符个数,并且大于容量时
//1、预留空间大于有效字符个数,并且大于容量时
string s1 = "xxxxxxxxxxxxx";
cout << "总容量大小(capacity):" << s1.capacity() << endl;
cout << "有效字符个数 (size):" << s1.size() << endl;
cout << "s1:" << s1 << endl;
s1.reserve(30);
cout << "预留空间:30!修改后--------------------------" << endl;
cout << "总容量大小(capacity):" << s1.capacity() << endl;
cout << "有效字符个数 (size):" << s1.size() << endl;
cout << "s1:" << s1 << endl;
>有效字符不变,容量增大(扩容方式在每个平台下可能会有不同,但结果一定是符合C++标准规定的!)
2、预留空间大于有效字符个数,但小于容量时
//2、预留空间大于有效字符个数,但小于容量时
string s1 = "xxxxxxxxxxxxx";
cout << "总容量大小(capacity):" << s1.capacity() << endl;
cout << "有效字符个数 (size):" << s1.size() << endl;
cout << "s1:" << s1 << endl;
s1.reserve(10);
cout << "预留空间:10!修改后--------------------------" << endl;
cout << "总容量大小(capacity):" << s1.capacity() << endl;
cout << "有效字符个数 (size):" << s1.size() << endl;
cout << "s1:" << s1 << endl;
>有效字符不变,容量不变!
3、预留空间小于有效字符个数,且小于容量时
3、预留空间小于有效字符个数,且小于容量时
string s1 = "xxxxxxxxxxxxx";
cout << "总容量大小(capacity):" << s1.capacity() << endl;
cout << "有效字符个数 (size):" << s1.size() << endl;
cout << "s1:" << s1 << endl;
s1.reserve(0);
cout << "预留空间:0!修改后--------------------------" << endl;
cout << "总容量大小(capacity):" << s1.capacity() << endl;
cout << "有效字符个数 (size):" << s1.size() << endl;
cout << "s1:" << s1 << endl;
>有效字符不变,容量不变!
4、clear(重要)
>clear()只是将string中有效字符清空,不改变底层空间大小。
string s1 = "xxxxxxxxxxxxx";
cout << "总容量大小(capacity):" << s1.capacity() << endl;
cout << "有效字符个数 (size):" << s1.size() << endl;
cout << "s1:" << s1 << endl;
s1.clear();
cout << "修改后--------------------------" << endl;
cout << "总容量大小(capacity):" << s1.capacity() << endl;
cout << "有效字符个数 (size):" << s1.size() << endl;
cout << "s1:" << s1 << endl<<endl;
cout << "***************" << endl;
5、empty(重要)
>判断字符串是否为空!
string s1 = "xxxxxxxxxxxxx";
cout << "总容量大小(capacity):" << s1.capacity() << endl;
cout << "有效字符个数 (size):" << s1.size() << endl;
cout << "s1:" << s1 << endl;
cout << s1.empty() << endl;
if (!s1.empty())
{
cout << "s1不为空"<<endl;
}
s1.clear();
cout << "修改后--------------------------" << endl;
cout << "总容量大小(capacity):" << s1.capacity() << endl;
cout << "有效字符个数 (size):" << s1.size() << endl;
cout << "s1:" << s1 << endl;
cout << s1.empty() << endl;
if (s1.empty())
{
cout << "s1为空"<<endl;
}
6、shrink_to_fit
>根据字符串的大小合理的调整容量的大小!
string s1 = "xxxxxxxxxxxxx";
s1.reserve(100);//先预留100个空间大小
cout << "总容量大小(capacity):" << s1.capacity() << endl;
cout << "有效字符个数 (size):" << s1.size() << endl;
cout << "s1:" << s1 << endl;
s1.shrink_to_fit();//再根据字符串的大小调整容量大小
cout << "修改后--------------------------" << endl;
cout << "总容量大小(capacity):" << s1.capacity() << endl;
cout << "有效字符个数 (size):" << s1.size() << endl;
cout << "s1:" << s1 << endl;
(需要用到直接查文档即可(链接都在下面),这里标注了一些重要的常用的接口)
2.5 string类的修改操作
1、operator+=(重要!)https://legacy.cplusplus.com/reference/string/string/operator+=/
2、append(在字符串后追加一个字符串)https://legacy.cplusplus.com/reference/string/string/append/
3、push_back(在字符串后尾插字符c)https://legacy.cplusplus.com/reference/string/string/push_back/
4、assign( 为字符串分配一个新值,替换其当前内容)https://legacy.cplusplus.com/reference/string/string/assign/
5、insert(在任意位置插入)https://legacy.cplusplus.com/reference/string/string/insert/
6、erase(删除字符串的部分)https://legacy.cplusplus.com/reference/string/string/erase/
7、replace(替换字符)https://legacy.cplusplus.com/reference/string/string/replace/
8、swap(交换俩个string)https://legacy.cplusplus.com/reference/string/string/swap/注意:string类的交换与库里面的swap有所不同,string类的交换是直接将两个指针进行了交换!
9,、pop_back (尾删一个字符)https://legacy.cplusplus.com/reference/string/string/pop_back/
2.6 string类的查找操作
1、c_str(获取C指针,重要)https://legacy.cplusplus.com/reference/string/string/c_str/
Get C string equivalent (public member function )
//C与C++ 的结合
string s1;
cin >> s1;
FILE* file = fopen(s1.c_str(), "r");//这里的参数必须是C字符串
char ch = fgetc(file);
while (ch!=EOF)
{
cout << ch;
ch = fgetc(file);
}
fclose(file);
file = nullptr;
2、data(与C_str作用一样,但一般不用这个接口)https://legacy.cplusplus.com/reference/string/string/data/
Get string data (public member function )
3、get_allocator(空间配置器,暂且不用了解)https://legacy.cplusplus.com/reference/string/string/get_allocator/
Get allocator (public member function )
4、copy(string拷贝)https://legacy.cplusplus.com/reference/string/string/copy/
Copy sequence of characters from string (public member function )
5、find(从pos位置向前查找子串或字符)https://legacy.cplusplus.com/reference/string/string/find/
Find content in string (public member function )
6、rfind(从pos位置向后查找)https://legacy.cplusplus.com/reference/string/string/rfind/
Find last occurrence of content in string (public member function )
Find character in string (public member function )
8、find_last_of(向后)https://legacy.cplusplus.com/reference/string/string/find_last_of/
Find character in string from the end (public member function )
Find absence of character in string (public member function )
10、find_last_not_of(向后)https://legacy.cplusplus.com/reference/string/string/find_last_not_of/
Find non-matching character in string from the end (public member function )
11、substr(返回子串)https://legacy.cplusplus.com/reference/string/string/substr/
Generate substring (public member function )
// string::find_last_of
#include <iostream> // std::cout
#include <string> // std::string
#include <cstddef> // std::size_t
void SplitFilename(const std::string& str)
{
std::cout << "Splitting: " << str << '\n';
//find_last_of从后向前找/或\其中任意字符并返回其下标
std::size_t found = str.find_last_of("/\\");
//C++中的区间都是左闭右开(取子串)
std::cout << " path: " << str.substr(0, found) << '\n';
std::cout << " file: " << str.substr(found + 1) << '\n';
}
int main()
{
std::string str1("/usr/bin/man");//Linux下的文件目录
std::string str2("c:\\windows\\winhelp.exe");//Windows下的文件目录
//分离路径与文件名
SplitFilename(str1);
SplitFilename(str2);
return 0;
}
Compare strings (public member function )
2.7 string类的非成员函数
operator+(重要)https://legacy.cplusplus.com/reference/string/string/operator+/
Concatenate strings (function )
relational operators(重要)https://legacy.cplusplus.com/reference/string/string/operators/
Relational operators for string (function )
swaphttps://legacy.cplusplus.com/reference/string/string/swap-free/
Exchanges the values of two strings (function )
operator>>(重要)https://legacy.cplusplus.com/reference/string/string/operator%3E%3E/
Extract string from stream (function )
operator<<(重要)https://legacy.cplusplus.com/reference/string/string/operator%3C%3C/
Insert string into stream (function )
getline(重要)https://legacy.cplusplus.com/reference/string/string/getline/
Get line from stream into string (function )
标注:和C中的gets的作用差不多,如果用cin或scanf读取字符串,在遇到空格或换行符时就会停止读取字符串。而getline就是针对string字符串解决这个问题的,并且它的功能还更加强大,可以自定义读取结束的条件!
string s1;
getline(cin, s1,'*');//指定读取结束条件!
cout << "读取结束!" << endl;
3. auto关键字
在这里补充2个C++11的小语法,方便我们后面的学习。
>在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,后来这个 不重要了。C++11中,标准委员会变废为宝赋予了auto全新的含义即 :auto不再是一个存储类型 指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期 推导而得。
>用auto声明指针类型 时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
// 编译报错:rror C3531: "e": 类型包含"auto"的符号必须具有初始值设定项
auto e;
int a = 0;
auto x = a;//int
auto y = &a;//int*
auto* z = &a;//int*
auto& w = a;//int
cout << "a:"<< typeid(a).name() << endl;
cout << "x:"<< typeid(x).name() << endl;
cout << "y:"<< typeid(y).name() << endl;
cout << "z:"<< typeid(z).name() << endl;
cout << "w:"<< typeid(w).name() << endl;
>当在同一行声明多个变量时 ,这些变量必须是相同的类型 ,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
int main()
{
auto a = 1, b = 2;//必须为同一类型
// 编译报错:error C3538: 在声明符列表中,"auto"必须始终推导为同一类型
auto a = 1, b = 2,c=3.0;//不同类型就报错
return 0;
}
>auto不能作为函数的参数 ,可以做返回值,但是建议谨慎使用
不可以作参数
可以做返回值,但是建议谨慎使用
auto func1()
{
auto a = 1;
return a;
}
auto func2()
{
return func1();
}
auto func3()
{
return func2;
}
auto func4()
{
return func3;
}
int main()
{
auto a = func4();
return 0;
}
看上面的一段代码,如果我们不往前看到第一个函数,我们知道最终a的类型是什么吗!所以我们把auto做返回值时一定要谨慎使用!
>auto不能直接用来声明数组
// 编译报错:error C3318: "auto []": 数组不能具有其中包含"auto"的元素类型
auto array[] = { 4, 5, 6 };
>auto的用武之地
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;
}
4. vs和g++下string结构的说明
4.1 vs下string的结构
**>注意:**下述结构是在32位平台下进行验证,32位平台下指针占4个字节。
>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;
class String
{
private:
//vs下string类里面的成员变量大概是这样
char _buff[16];
char* str;
size_t _size;
size_t capacity;
};
int main()
{
cout<<sizeof(String)<<endl;
return 0;
}
>这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建 好之后,内部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。
>其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量
>最后:还有一个指针做一些其他事情。 故总共占16+4+4+4=28个字节。
>vs下string扩容
void TestPushBack()
{
string s;
size_t sz = s.capacity();
cout << "原始大小:" << sz << endl;
cout << "making s grow:" << endl;
for (int i = 0; i < 100; i++)
{
s.push_back('c');
if (sz != s.capacity())
{
sz = s.capacity();
cout << "capacity change:" << sz << "\n";
}
}
}
>从结果来看,我们可以知道在vs下,当字符串的大小超过16时,vs是两倍扩容的,之后的每一次都是1.5倍扩容的!
4.2 g++下string的结构
>G++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个 指针,该指针将来指向一块堆空间,内部包含了如下字段:
1、空间总大小
2、字符串有效长度
3、引用计数
struct _Rep_base
{
size_type _M_length;
size_type _M_capacity;
_Atomic_word _M_refcount;
};
4、指向堆空间的指针,用来存储字符串。
>g++下string的扩容
>我们可以看到,在g++下string是两倍扩容的!
5. 完结散花
好了,这期的分享到 这里就结束了~
如果这篇博客对你有帮助的话,可以用你们的小手指点一个免费的赞并收藏起来哟~
如果期待博主下期内容的话,可以点点关注,避免找不到我了呢~
我们下期不见不散~~