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

🌈个人主页:秋风起,再归来~
🔥系列专栏: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(14);
cout << "预留空间:14!修改后--------------------------" << 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_tvoid 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;//intauto y = &a;//int*auto* z = &a;//int*auto& w = a;//intcout << "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 onevalue_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.mzph.cn/news/879160.shtml

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

相关文章

Vulnhub-RickdiculouslyEasy靶机攻略

御剑扫描到ip 一.第一个flag 主机扫描 目录扫描 二.网页信息收集-第二个flag 9090也开放了web服务所以我们在IP地址后面加端口试试&#xff0c;如下图&#xff0c;加上了端口&#xff0c;并且发现了第二个flag&#xff0c;也对第二个flag进行了简单的探索也没有发现什么可以…

内存卡提示格式化怎么办?轻松应对格式化

在日常使用电子设备时&#xff0c;我们有时会遇到内存卡提示需要格式化的情况。这种情况往往让人感到焦虑和困惑&#xff0c;因为格式化意味着内存卡上的所有数据都将被清除。然而&#xff0c;在遇到这种情况时&#xff0c;我们不必过于惊慌。本文将介绍四种方法&#xff0c;帮…

如何获取MySQL数据表的列信息

在数据库管理中&#xff0c;了解表的结构是至关重要的。在MySQL中&#xff0c;我们可以通过几种方式来获取数据表的列信息。这不仅可以帮助我们更好地理解表的结构&#xff0c;还可以在编写查询时提供便利。以下是三种常用的方法来获取MySQL数据表的列信息。 使用 SHOW COLUMN…

UDP通信实现

目录 前言 一、基础知识 1、跨主机传输 1、字节序 2、主机字节序和网络字节序 3、IP转换 2、套接字 3、什么是UDP通信 二、如何实现UDP通信 1、socket():创建套接字 2、bind():绑定套接字 3、sendto():发送指定套接字文件数据 4、recvfrom():接收指定地址信息的数据 三…

如何逆转Instagram账号流量减少?4个实用技巧分享

Instagram作为全球十大社媒之一&#xff0c;不仅是个人分享生活的平台&#xff0c;还是跨境卖家进行宣传推广和客户开发的关键工具。在运营Instagram的过程中&#xff0c;稍有不慎就容易出现账号被限流的情况&#xff0c;对于账号状态和运营工作的进行都十分不利。 一、如何判断…

怎么调整图片亮度?关于调整图片亮度的几种方法推荐

怎么调整图片亮度&#xff1f;调整图片亮度是图像编辑中的一项基本但至关重要的操作。亮度直接影响图像的视觉效果和清晰度&#xff0c;它能够改变照片的整体氛围和表现力。无论你是在处理个人拍摄的照片、编辑设计作品&#xff0c;还是进行专业的图像修复&#xff0c;适当的亮…

API安全 | 发现API的5个小tips

在安全测试目标时&#xff0c;最有趣的测试部分是它的 API。API 是动态的&#xff0c;它们比应用程序的其他部分更新得更频繁&#xff0c;并且负责许多后端繁重的工作。在现代应用程序中&#xff0c;我们通常会看到 REST API&#xff0c;但也会看到其他形式&#xff0c;例如 Gr…

adb有线连接正常,adb connect失败

adb connect失败 1. 确认两个设备在同一个局域网 2. 确认此网络是否有adb连接的权限(有的公司网络不允许adb) 3. 确认防火墙设置 如果前面3步都确认没问题&#xff0c;Ping ip也能成功&#xff0c;那么有可能就是端口的问题: step1&#xff1a; 先用有线连接设备&#xff0…

macos系统内置php文件列表 系统自带php卸载方法

在macos系统中, 自带已经安装了php, 根据不同的macos版本php的版本号可能不同, 我们可以通过 which php 命令来查看mac自带的默认php安装路径, 不过注意这个只是php的执行文件路径. 系统自带php文件列表 一下就是macos默认安装的php文件列表. macos 10.15内置PHP文件列表配置…

java微信机器人制作教程

Java实现微信小号做机器人 随着人工智能技术的发展&#xff0c;机器人在各行各业扮演着越来越重要的角色。在社交领域&#xff0c;微信机器人也逐渐受到人们的关注。本文将介绍如何使用Java实现一个简单的微信小号做机器人的功能。 常见开发功能&#xff1a; 好友管理&#…

css 个人喜欢的样式 速查笔记

起因&#xff0c; 目的: 记录自己喜欢的&#xff0c; 觉得比较好看的 css. 下次用的时候&#xff0c;直接复制&#xff0c;很方便。 1. 个人 html 模板&#xff0c; 导入常用的 link 设置英语字体: Noto导入默认的 css使用网络 icon 图标导入 Bootstrap css 框架 html <…

简单好用的OCR API

现如今&#xff0c;越来越多的科技产品可以帮助我们改善和提高相应的工作效率。OCR技术的出现&#xff0c;提高了人们的工作效率&#xff0c;其应用领域及其广泛。就拿应用了OCR技术的翔云文档识别服务来说&#xff0c;只需上传文档图片便可自动识别并返回文档中相应的内容。翔…

vue+IntersectionObserver + scrollIntoView 实现电梯导航

一、电梯导航 电梯导航也被称为锚点导航&#xff0c;当点击锚点元素时&#xff0c;页面内相应标记的元素滚动到视口。而且页面内元素滚动时相应锚点也会高亮。电梯导航一般把锚点放在左右两侧&#xff0c;类似电梯一样。 二、scrollIntoView() 介绍 scrollIntoView() 方法会…

erlang学习: Mnesia Erlang数据库2

Mnesia数据库增加与查询学习 -module(test_mnesia).-record(shop, {item, quantity, cost}). -record(cost, {name, price}). -record(design, {info, plan}). %% API -export([insert/3,select/1,start/0]). start() ->mnesia:start().insert(Name, Quantity, Cost) ->…

k8s的Ingress控制器安装

Ingress文档地址&#xff1a;Ingress文档 1.安装helm 官网地址&#xff1a;helm官网安装 wget https://get.helm.sh/helm-v3.2.3-linux.amd64.tar.gz tar -zxvf helm-v3.2.3-linux-amd64.tar.gz cp linux-amd64/helm /usr/local/bin/ rootmaster01:~# helm version version.B…

《python语言程序设计》2018版第8章第14题金融:信用卡号合法性 利用6.29题

一、之前6.29题我做的代码 这是用数字来进行分辨的 is_txt 4383576018402626 #合法def split_the_data_even(vis_n):current_a1 vis_n // 10000a_t1 vis_n % 10000# print("1th", a_t1)a_t2 current_a1 % 10000# print("2th", a_t2)current_a3 curre…

Python设计模式实战:开启软件设计的精进之旅

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

python绘制3D瀑布图

成品&#xff1a; 代码&#xff1a; def line_3d(x, y, z, x_label_indexs):"""在y轴的每个点&#xff0c;向x轴的方向延伸出一个折线面&#xff1a;展示每个变量的时序变化。x: x轴&#xff0c;时间维&#xff0c;右边。y: y轴&#xff0c;变量维&#xff0c;…

《OpenCV计算机视觉》—— 图像形态学(腐蚀、膨胀等)

文章目录 一、图像形态学基本概念二、基本运算1.简单介绍2.代码实现 三、高级运算1.简单介绍2.代码实现 一、图像形态学基本概念 图像形态学是图像处理科学的一个独立分支&#xff0c;它基于集合论和数学形态学的理论&#xff0c;专门用于分析和处理图像中的形状和结构。图像形…

linux学习之线程2:线程控制与使用

铺垫 之前我们提到&#xff0c;Linux不直接对线程进行调度&#xff0c;而是对轻量级进程进行调度。但用户就想像Windows那样直接对线程进程控制。所以&#xff0c;就有了pthread库来封装了一层。 那么想要进行线程控制&#xff0c;要用pthread库。&#xff08;pthread库是原生…