C++从入门到起飞之——string类用法 全方位剖析!

🌈个人主页:秋风起,再归来~****
🔥系列专栏: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) 空字符串构造函数(默认构造函数)(重要))

(2)拷贝构造函数(重要)

[(3) substring(子串,子链) 构造函数](#(3) substring(子串,子链) 构造函数)

[(4) 常量字符串构造函数(重要)](#(4) 常量字符串构造函数(重要))

[(5) 拷贝字符串前n个构造函数](#(5) 拷贝字符串前n个构造函数)

[(6) 填充构造函数](#(6) 填充构造函数)

[2.3 string类可用的三种遍历方式](#2.3 string类可用的三种遍历方式)

(1)下标访问遍历

(2)迭代器遍历(含4种迭代器用法详解)

(3)范围for遍历

[2.4 string类的容量操作](#2.4 string类的容量操作)

1、max_size

[2、resize (重点)](#2、resize (重点))

3、reserve(重要)

4、clear(重要)

5、empty(重要)

6、shrink_to_fit

(需要用到直接查文档即可(链接都在下面),这里标注了一些重要的常用的接口)

[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类的文档介绍

>在使用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 太短或 lenstring::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);

>用字符 cn 个连续副本填充字符串。

	//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 )

7、find_first_of(从pos位置向前查找所指定的任意字符)https://legacy.cplusplus.com/reference/string/string/find_first_of/

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 )

9、find_first_not_of(向前查找除指定字符外的任意字符)https://legacy.cplusplus.com/reference/string/string/find_first_not_of/

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;
}

12、compare(比较string,基本不用,因为有更好用的比较运算符重载)https://legacy.cplusplus.com/reference/string/string/compare/

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. 完结散花

好了,这期的分享到 这里就结束了~

如果这篇博客对你有帮助的话,可以用你们的小手指点一个免费的赞并收藏起来哟~

如果期待博主下期内容的话,可以点点关注,避免找不到我了呢~

我们下期不见不散~~

​​​​

相关推荐
XiaoLeisj2 小时前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
励志成为嵌入式工程师3 小时前
c语言简单编程练习9
c语言·开发语言·算法·vim
捕鲸叉3 小时前
创建线程时传递参数给线程
开发语言·c++·算法
A charmer3 小时前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法
Peter_chq3 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
记录成长java5 小时前
ServletContext,Cookie,HttpSession的使用
java·开发语言·servlet
前端青山5 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js
青花瓷5 小时前
C++__XCode工程中Debug版本库向Release版本库的切换
c++·xcode
睡觉谁叫~~~5 小时前
一文解秘Rust如何与Java互操作
java·开发语言·后端·rust
音徽编程5 小时前
Rust异步运行时框架tokio保姆级教程
开发语言·网络·rust