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

news2024/12/23 14:10:15

🌈个人主页:秋风起,再归来~
🔥系列专栏:C++从入门到起飞          
🔖克心守己,律己则安

目录

1. 为什么学习string类?

1.1 C语言中的字符串

1.2 两个面试题(先不做讲解)

2. 标准库中的string类

2.1 string类(了解)

2.2 string类常用的构造函数

(1) 空字符串构造函数(默认构造函数)(重要)

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

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

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

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

 (6) 填充构造函数

 2.3 string类可用的三种遍历方式

(1)下标访问遍历

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

(3)范围for遍历

2.4 string类的容量操作

1、max_size

2、resize (重点)

 3、reserve(重要)

4、clear(重要)

5、empty(重要)

 6、shrink_to_fit

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

2.5 string类的修改操作

2.6 string类的查找操作

2.7 string类的非成员函数

3. auto关键字

4. vs和g++下string结构的说明 

4.1 vs下string的结构

4.2 g++下string的结构

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 太短或 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+=(重要!)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/string/string/operator+=/

2、append(在字符串后追加一个字符串)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/string/string/append/

3、push_back(在字符串后尾插字符c)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/string/string/push_back/
4、assign( 为字符串分配一个新值,替换其当前内容)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/string/string/assign/

5、insert(在任意位置插入)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/string/string/insert/

6、erase(删除字符串的部分)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/string/string/erase/

7、replace(替换字符)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/string/string/replace/

8、swap(交换俩个string)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/string/string/swap/注意:string类的交换与库里面的swap有所不同,string类的交换是直接将两个指针进行了交换!

9,、pop_back (尾删一个字符)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/string/string/pop_back/

2.6 string类的查找操作

1、c_str(获取C指针,重要)icon-default.png?t=N7T8https://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作用一样,但一般不用这个接口)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/string/string/data/

Get string data (public member function )

3、get_allocator(空间配置器,暂且不用了解)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/string/string/get_allocator/

Get allocator (public member function )

4、copy(string拷贝)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/string/string/copy/

Copy sequence of characters from string (public member function )

5、find(从pos位置向前查找子串或字符)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/string/string/find/

Find content in string (public member function )

6、rfind(从pos位置向后查找)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/string/string/rfind/

Find last occurrence of content in string (public member function )

7、find_first_of(从pos位置向前查找所指定的任意字符)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/string/string/find_first_of/

Find character in string (public member function )

8、find_last_of(向后)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/string/string/find_last_of/

Find character in string from the end (public member function )

9、find_first_not_of(向前查找除指定字符外的任意字符)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/string/string/find_first_not_of/

Find absence of character in string (public member function )

10、find_last_not_of(向后)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/string/string/find_last_not_of/

Find non-matching character in string from the end (public member function )

11、substr(返回子串)icon-default.png?t=N7T8https://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,基本不用,因为有更好用的比较运算符重载)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/string/string/compare/

Compare strings (public member function ) 

2.7 string类的非成员函数

operator+(重要)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/string/string/operator+/

Concatenate strings (function )

relational operators(重要)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/string/string/operators/

Relational operators for string (function )

swapicon-default.png?t=N7T8https://legacy.cplusplus.com/reference/string/string/swap-free/

Exchanges the values of two strings (function )

operator>>(重要)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/string/string/operator%3E%3E/

Extract string from stream (function )

operator<<(重要)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/string/string/operator%3C%3C/

Insert string into stream (function )

getline(重要)icon-default.png?t=N7T8https://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. 完结散花

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

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

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

我们下期不见不散~~

​​​​

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1975027.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IMYAI智能助手介绍:全能AI工具箱,AI爱好者AIGC系统的首选。

IMYAI系统官方网站&#xff1a;https://new.imyai.top 引言&#xff1a; IMYAI系统是一款集多种先进AI技术于一身的创作工具箱&#xff0c;为用户提供无限的创作可能。不论您是想创作精彩的文章&#xff0c;还是创作精美的绘画作品&#xff0c;IMYAI系统都能满足您的需求。它集…

不想要反馈的错误遮挡页面,,想让它在控制台提示

开始页面是这样的 然后在vue.config.js 中修改 devServer: {client: {//当出现编译错误或警告时&#xff0c;在浏览器中是否显示全屏覆盖。 示例为只显示错误信息overlay: {runtimeErrors: false,},},},然后关闭服务&#xff0c;重新运行项目。 结果

【Netty】netty启动流程源码解析

文章目录 Netty整体架构一个启动流程源码解析new NioEventLoopGroup(1)构建线程池基础信息构建线程选择策略 groupchannelhandlerchildHandlerbindinitAndRegister反射创建 NioServerSocketChannel 对象init 注册channeldoBind0 流程图思考 Netty整体架构 是什么&#xff1a; N…

【学习方法】高效学习因素 ① ( 开始学习 | 高效学习因素五大因素 | 高效学习公式 - 学习效果 = 时间 x 注意力 x 精力 x 目标 x 策略 )

文章目录 一、高效学习因素1、开始学习2、高效学习因素五大因素3、高效学习公式 - 学习效果 时间 x 注意力 x 精力 x 目标 x 策略 一、高效学习因素 1、开始学习 对于 学习差 , 调皮捣蛋 的学生 , 不要把 学习成绩差 的 原因 归因为 不爱学习 / 没有学习方法 , 可能是 还没有 …

Docker-学习笔记(借助宝塔面板)

ubuntu环境 一、安装 可以参考官网进行或其他博客进行安装 1.进入宝塔面板 进图Docker菜单&#xff0c;查看是否提示安装。 2.查看是否安装 查看版本 docker -v 证明已经安装 二、常用命令 1.查看版本 docker -v 2.启动、停止、重启docker systemctl start docker…

windows C++-通过 C++/WinRT 使用 API(一)

本文介绍如何使用 C/WinRT API&#xff0c;无论它们是 Windows 的一部分、由第三方组件供应商或自行实现。 本文中的代码示例较短&#xff0c;并且很容易试验&#xff0c;可以通过创建新的 Windows 控制台应用程序 (C/WinRT) 项目和复制粘贴代码来重现它们。 但是&#xff0c;…

【Redis】浅谈架构和认识Redis

目录 架构演进 单机架构 应用数据分离架构 应用服务集群架构 读写分离/主从分离架构 冷热分离架构&#xff08;引入缓存&#xff09; 垂直分库 微服务架构 认识Redis Redis的特性 架构演进 单机架构 简单来说就是只有一台服务器&#xff0c;这个服务器用来负责所有…

GlobalMapper方量计算(两期地形对比,提取填挖方区域及每个区域的方量)

0.序 在工程设计中&#xff0c;经常需要根据设计方案和现状地形之间进行方量计算&#xff0c;尤其关注方量变化的区域&#xff0c;哪些区域需要填方&#xff0c;哪些区域需要挖方&#xff0c;并依据此进行方量的平衡。 在流域管理中&#xff0c;尤其是湿地、三角洲等容易淤积或…

详细分析Java中的SPI机制(附Demo)

目录 前言1. 基本知识2. Demo3. 解读源码 前言 相关的Java知识推荐阅读&#xff1a; java框架 零基础从入门到精通的学习路线 附开源项目面经等&#xff08;超全&#xff09;【Java项目】实战CRUD的功能整理&#xff08;持续更新&#xff09; 1. 基本知识 SPI&#xff08;S…

PANDA:免微调提升大型语言模型领域特定能力的新方法

人工智能咨询培训老师叶梓 转载标明出处 大模型虽然在广泛的任务上具有通用性&#xff0c;但在面对特定领域的任务时&#xff0c;它们的性能往往不如专门为这些领域训练的模型。传统的知识蒸馏&#xff08;Knowledge Distillation, KD&#xff09;方法通过资源密集型的微调过程…

怎么在电脑上查找打印机打印过的文件?告别翻箱倒柜!电脑查找已打印文件技巧公示!

在日常办公中&#xff0c;我们经常会使用打印机来输出各种文件&#xff0c;但有时候&#xff0c;我们可能需要回顾或查找之前打印过的文件。然而&#xff0c;这些文件一旦打印完成&#xff0c;往往就离开了我们的电脑屏幕&#xff0c;进入了纸质世界&#xff0c;而电子文件可能…

Tree-of-Traversals:结合知识图谱与大模型,通过树遍历和回溯寻找高置信度推理路径

Tree-of-Traversals&#xff1a;结合知识图谱与大模型&#xff0c;通过树遍历和回溯寻找高置信度推理路径 Tree-of-Traversals算法解析对比 MindMap1. 与知识图谱&#xff08;KGs&#xff09;的整合2. 推理方法3. 灵活性与可扩展性4. 在医学诊断中的应用 速度和准确1. 速度2. 推…

数据结构第九讲:二叉树

数据结构第九讲&#xff1a;二叉树 1.实现链式结构二叉树1.1二叉树的节点结构1.2创建二叉树节点1.3前中后序遍历1.3.1前序遍历1.3.2中序遍历1.3.3后序遍历1.3.4总结 1.4二叉树结点的个数1.4.1错误示范1.4.2实现方法 1.5二叉树叶子结点的个数1.6二叉树第k层结点的个数1.7二叉树的…

看门狗应用编程-I.MX6U嵌入式Linux C应用编程学习笔记基于正点原子阿尔法开发板

看门狗应用编程 看门狗应用编程介绍 看门狗定时器的基本概念 看门狗是一个可以在一定时间内被复位/重置的计数器 如果在规定时间内没有复位&#xff0c;看门狗计时器溢出会对CPU产生复位信号使系统重启 有些看门狗可以只产生中断信号而不会使系统复位 I.MX6UL/I.MX6ULL So…

如何减少内存碎片的产生——页

文章目录 1 页的设计目的2 进程块和主存块的对应关系3 页、页框、页表3.1 页&#xff08;Page&#xff09;3.2 页框&#xff08;Page Frame&#xff09;3.3 页表&#xff08;Page Table&#xff09; 4 逻辑地址到物理地址的转换4.1 转换过程4.2 具体示例4.3 图示 参考资料封面 …

C语言程序设计25

《C程序设计教程&#xff08;第四版&#xff09;——谭浩强》 习题2.2 分析下面程序的运行结果&#xff0c;然后上机验证。 代码&#xff1a; //《C程序设计教程&#xff08;第四版&#xff09;——谭浩强》 //习题2.2 分析下面程序的运行结果&#xff0c;然后上机验证。#inc…

【C语言篇】操作符详解(下篇)

文章目录 操作符详解&#xff08;下篇&#xff09;前言条件操作符移位操作符左移操作符右移操作符 位操作符下标引用操作符函数调用操作符 操作符的属性&#xff1a;优先级和结合性优先级结合性表达式求值整形提升算术转换 操作符详解&#xff08;下篇&#xff09; 前言 操作…

JavaScript基础——JavaScript常见语句(判断语句、循环语句、控制流语句)

JavaScript提供了丰富的语句来控制程序的执行流程&#xff0c;包括用于条件判断的if、switch和三元运算符&#xff0c;以及用于循环的for、while、do...while、for...in和for...of。此外&#xff0c;还有控制流语句如break、continue和return。 判断语句 if 语句 if 语句&…

C/C++开发,opencv轮廓提取实现

一、cv::findContours轮廓提取函数 1.1 cv::findContours函数简介 cv::findContours 函数是用于从二值图像&#xff08;灰度图&#xff09;中检索轮廓。这个函数在OpenCV的不同版本中参数可能有所不同&#xff0c;但基本概念保持一致。特别是在OpenCV 3.x和4.x版本中&#xff…

贪吃蛇(使用QT)

贪吃蛇小游戏 一.项目介绍**[贪吃蛇项目地址](https://gitee.com/strandingzy/QT/tree/zyy/snake)**界面一&#xff1a;游戏大厅界面二&#xff1a;关卡选择界面界面三&#xff1a;游戏界面 二.项目实现2.1 游戏大厅2.2关卡选择界面2.3 游戏房间2.3.1 封装贪吃蛇数据结构2.3.2 …