[Elementary C++] 9. String class in STL container (Part 1)

=========================================================================

Relevant code can be obtained from gitee :

C language learning diary: keep working hard (gitee.com)

 =========================================================================

Continuing from the previous issue :

[Elementary C++] 8. First introduction to templates (generic programming, function templates, class templates) - CSDN Blog

 =========================================================================

                     

Table of contents

1. Introduction to STL

What is STL

STL version

Original HP version:

PJ version:

RW version:

SGI version:

The six major components of STL

Illustration:


2. string class

String in C language

The string class in the C++ standard library

string class (understand)

Summarize:

Description of common interfaces of string class (key points)

Common constructors for string class objects:

Illustration--the first constructor:

Illustration--The 2nd and 3rd constructors:

Illustration--the 4th and 5th constructors:

Illustration--6th and 7th constructors:

Supplement--operator=: (assignment "=" operator overloaded function)

Capacity operations on string class objects:

Precautions for use:

Access to string class objects is a traversal operation:

operator[]: (Subscript operator "[ ]" overloaded function)

begin + end: (traversing through iterator)

rbegin + rend: (reverse traversal through iterator)

Range for loop--complete string traversal:


Code related to this blog:

Test.cpp file--C++ file:

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

1. Introduction to STL

What is STL

                    

STL ( standard template libaray - standard template library ):

It is an important part of the C++ standard library . It is not only a reusable component library , but also a software framework including data structures and algorithms .

                     

                     


                    

STL version

                   

Original HP version:

The original version was completed by Alexander Stepanov and Meng Lee at HP Labs . In the spirit of open source , they stated that anyone is allowed to use , copy , modify , disseminate , and commercially use these codes without paying . The only condition is that they need to be the same as the original version. For open source use . HP version of STL - the ancestor of all STL implementations


                          

                          
---------------------------------------------------------------------------------------------

                       

PJ version:

Developed by PJ Plauger , inherited from the HP version , adopted by  Windows Visual C++ ( VS series ) , and cannot be made public or modified . Disadvantages : low readability , weird naming of symbols 

                          

                          
---------------------------------------------------------------------------------------------

                       

RW version:

Developed by  Rouge Wage Company , inherited from HP version , adopted by C++ Builder , cannot be disclosed or modified , and has average readability

                          

                          
---------------------------------------------------------------------------------------------

                       

SGI version:

Developed by  Silicon Graphics Computer Systems , inc ., it is inherited from the HP version . Adopted by GCC ( Linux ) , it has good portability , can be made public , can be modified and even sold . From the naming style and programming style , it is very readable .

                     

                     


                    

The six major components of STL

              

  • The six major components of STL are :
    functors , algorithms , iterators , spatial configurators , containers , and adapters
Illustration:

                   

  • The space allocator is also called a memory pool . The container can be simply understood as a data structure . The adapter can also be called an adapter .
    The container ( data structure ) requires a large amount of space to store data .
    If you frequently apply for a large amount of memory space from the heap , the efficiency will be a bit slow. Low , so there is a space allocator ( memory pool ) in STL , which specifically provides memory space for containers ( data structures ) . The initialization of the memory space is completed by positioning new.

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

2. string class

String in C language

           

In C language , a string is a collection of characters ending with ' \ 0 ' . For convenience of operation , the C language standard library provides some str series library functions , but these library functions are separated from strings and cannot It is very consistent with the idea of ​​​​OOP ( object-oriented programming ) , and the underlying space needs to be managed by the user himself . If you are not careful, you may access out of bounds.


                     

                     


                    

The string class in the C++ standard library

             

string class (understand)

                

  • Strictly speaking, the string class is not an STL container , but from the perspective of classification , the string can actually be regarded as a string in the data structure , a string that specifically manages characters . Because string was created earlier than STL , it is included in the standard library in the C++ document instead of STL .


                        
  • stringA string is a class that represents a sequence of characters . Although we usually call it the string class ,
    string is actually an instance of a template class .
                       
  • Support for such objects is provided by the standard String class , whose interface is similar to that of the standard character container , but with the addition of design features specifically for manipulating single-byte character strings.

                      
  • The string class uses char as its character type , using its default char_traits and allocator types
    (for more information on templates , see: C++ string class templates )
                
  • The string class is an example of the basic_string template class . It uses char to instantiate the basic_string template class , and uses char_traits and allocator as the default parameters of basic_string (for more information on templates , please refer to: C++ string class template )


                 
  • Note :
    The string class handles bytes independently of the encoding used . If used to handle sequences of multibyte or variable-length characters ( such as UTF-8 ) , all members of the string class ( such as length or size ) and its iterations processor , will still operate in terms of bytes rather than actual encoded characters



                  
Summarize:
  • string is the string class that represents strings
              
  • The interface of this class is basically the same as that of a regular container , and some regular operations for manipulating strings are added.
                 
  • The string class is actually an alias ( reference ) of the basic_string template class at the bottom , using typedef basic_string <char, char_traits, allocator> string ; renamed to string

                     
  • Cannot operate on multi-byte or variable-length character sequences
                  
  • When using the string class , you must include the #include<string> header file and using namespace std; (you do not need to fully expand it , just expand the part corresponding to the string )

                         

                          
---------------------------------------------------------------------------------------------

                     

Description of common interfaces of string class (key points)

              

Common constructors for string class objects:
( constructor ) constructor function name Corresponding function description
string ();        (emphasis) Creates a string object
without using string initialization
string (const char* s );        (emphasis) Create a string object
and initialize it with a constant string
string (const string& str );        (emphasis) Create a string object
and initialize it with another string object
string (const string& str, size_t pos, size_t len = npos); Copy the len characters starting from the pos subscript in the str
string and
copy them to the created string object.
string (const char* s, size_t n); Copies n characters of a constant string
to
the created string object
string (size_t n, char c); Fill n c characters into
the created string object
template <class InputIterator> string (InputIterator first, InputIterator last); It involves iterators ,
I don’t understand, I’ll talk about it later.
Illustration--the first constructor:

Illustration--The 2nd and 3rd constructors:

Illustration--the 4th and 5th constructors:

Illustration--6th and 7th constructors:

Supplement--operator=:
(assignment "=" operator overloaded function)
  • String class objects can also be initialized through the assignment " = " operator overloaded function ( operator= )
                       
  • There are three implementations of operator= :
    Type 1 : string& operator= ( const string& str ); --  String string class object assignment
    Type 2 : string& operator= ( const char* s ); --   Directly write out the string
    The third type of assignment : string& operator= ( char c ); --Single   character assignment

Corresponding icon :

                          

                          
---------------------------------------------------------------------------------------------

                       

Capacity operations on string class objects:
function name Function Description
size         (emphasis) Returns the effective character size ( length ) of the string
length Returns the valid character length of the string
capacity The number of valid characters that can actually be stored
empty       (emphasis) Detects whether the string is an empty string , returns true if it is , otherwise returns false
clear       (emphasis) Clear valid characters in a string
reserve      (emphasis) Reserve space for strings , which can be expanded
(only affects capacity , not data)
resize       (emphasis) Divide the number of valid characters into n , and the extra space can be filled with character c
(that is , it affects the capacity and the data )
Precautions for use:
  • The underlying implementation principles of the size() and length() methods are exactly the same . The reason for introducing size() is to be consistent with the interfaces of other containers . In general, the corresponding icon of size() is basically used :


                   
  • The clear() method only clears the valid characters in the string string and does not change the underlying space size.

                   
  • resize ( size_t n ) and resize ( size_t n , char c ) both change the number of valid characters in the string to n . The difference is that when the number of characters increases : resize ( size_t n ) uses 0 to fill the excess element space , resize ( size_t n , char c ) uses character c to fill the extra element space . Note: When resize changes the number of elements , if the number of elements is increased , the size of the underlying capacity may be changed . If the number of elements is reduced , the total size of the underlying space remains unchanged .





Illustration -- resize:

                  

  •  reserve ( size_t res_arg=0 ): Reserve space
    for string without changing the number of valid elements . When the parameter of reserve is less than the total size of the underlying space of string , reserve will not change the capacity size .

Illustration -- reserve:

                     

Illustration - reserve capacity expansion:

                          

                          
---------------------------------------------------------------------------------------------

                       

Access to string class objects is a traversal operation:
function name Function Description
operator[]         (emphasis) Returns the character at the pos subscript position . Const string class objects also have corresponding const calls.
begin + end begin : Get the iterator of the first character end : Get the iterator of the next position of the last character
rbegin  +  rend being + end is a forward iterator ( forward traversal), which is divided into const and non-const versions.
rbegin + rend is a reverse iterator ( reverse traversal), which is also divided into const and non-const versions.
range for loop C++11 supports a new , simpler traversal method for range for loops
operator[]:
(Subscript operator "[ ]" overloaded function)
  • There are two implementations of " operator[] " of the string class : Type 1 : char& operator[] ( size_t pos ); Type 2 : const char& operator[] ( size_t pos ) const ;


                   
  • After the string class implements " operator[] " , the string class object can access the characters in the string like an array through subscripts .

Illustration--first implementation :

begin + end:
(traversing through iterator)
  • Iterator is defined in the class domain , but it is not an internal class , it is a type .
    Now that you are new to iterators , you can think that the usage of iterators is similar to pointers.
                             
  • The begin() and end() methods of the iterator form an interval that is " left closed and right open " . For string objects , begin() can be understood as a pointer to the first character of the string , and end() can be understood as a pointer to the first character of the string . Pointer to the next character after the last valid character

Illustration - using an iterator to traverse a string :

                  

Illustration - the second implementation of iterator operator[] :

rbegin + rend:
(reverse traversal through iterator)
  • Similar to begin+end , except that rbegin+rend traverses in reverse
                             
  • begin+end and rbegin+rend each have two implementations :
    non-const version and const version

Illustration - using an iterator to traverse a string in reverse :

Range for loop--complete string traversal:
  • The bottom layer of the range for loop is implemented through iterators , which is a more concise new traversal method of the range for loop supported by C++11.

Corresponding icon :

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

Code related to this blog:

Test.cpp file--C++ file:

#define _CRT_SECURE_NO_WARNINGS 1

//包含IO流头文件:
#include<iostream>
//完全展开std命名空间:
using namespace std;

//包含string类头文件:
#include<string>

#include<vector>
#include<list>

//template<class T>
//T* func(int n)
//{
//	return new T[n];
//}
//
主函数:
//int main()
//{
//	func<double> (10);
//	
//	return 0;
//}



string类的默认成员函数:
主函数:
//int main()
//{
//	//string:
//	/*
//	* string是一个类模板,
//	* typedef basic_string<char> string;
//	* 原名是叫 basic_string<char> 的类模板,
//	* 被typedef重命名为string
//	*/
//
//	//string类的默认成员函数 -- 构造函数:
//	//string类中有7种构造函数:
//	/*
//	*(最常用)1:无参的string -- string(); 
//	*(最常用)2:string (const char* s);
//	* 3:string (const string& str);
//	* 4:string (const string& str, size_t pos, size_t len = npos);
//	* 5:string (const char* s, size_t n);
//	* 6:string (size_t n, char c);
//	* 7:template <class InputIterator> string (InputIterator first, InputIterator last);
//	*/
//
//	//(最常用)1:无参的string -- string(); 
//	string s1;
//
//
//	//(最常用)2:string (const char* s);
//	string s2("hello world");
//	/*
//	* s:接收一个字符串
//	* 创建一个字符串类,
//	* 并使用C语言的常量字符串的地址进行初始化,
//	* s2就存储着这段常量字符串
//	*/
//
//
//	//赋值“=”,调用拷贝构造初始化:
//	string s3 = s2;
//	/*
//	* 该写法相当于4中的:string s3(s2);
//	*/
//	
//
//	//3:string (const string& str);
//	string s4(s3);
//	/*
//	* str:接收string字符串类对象
//	* 拷贝str字符串对象,
//	* 该写法相当于3中的:string s4 = s3;
//	* 这种方法也是调用拷贝构造函数进行初始化的
//	*/
//
//	/*
//	* 因为string类重载了 流插入/流提取:
//	*	operator << / operator >>
//	* 所以是可以直接使用 输出流/输入流 的
//	*/
//	cout << "构造函数1:" << s1 << endl;
//	cout << "构造函数2:" << s2 << endl;
//	cout << "赋值“=”:" << s3 << endl;
//	cout << "构造函数3:" << s4 << endl;
//
//
//	//4:string (const string& str, size_t pos, size_t len = npos);
//	/*
//	* str:接收string字符串类对象
//	* pos:下标
//	* len:长度
//	* npos:一个整型静态const无符号的变量,值为-1,
//	*		因为无符号,-1会是整型的最大值,
//	*		所以如果不对len初始化的话,len的缺省值就是很大的值,
//	*		所以会拷贝很长的字符串,即拷贝pos后的所有字符了
//	*		(len不给默认拷贝str中pos下标后的所有字符)
//	* 
//	* 这个string类函数的功能是拷贝字符串str的一部分,
//	* 从pos下标开始,拷贝len长度的字符串
//	*/
//	string s5(s2, 1, 5);
//	/*
//	* 从s2字符串的第1个字符开始,
//	* (s2:“hello world”)
//	* 往后拷贝5个字符长度的字符串,
//	* 拷贝结果存放在字符串s5中。
//	* (s5:“ello ”)
//	* 注:空格也算一个字符;
//	*     len如果超过str的长度,则str结尾为止
//	*/
//	cout << "构造函数4:" << s5 << endl;
//	
//
//	//5:string (const char* s, size_t n);
//	/*
//	* s:字符串指针
//	* n:在s字符串中拷贝字符长度
//	*/
//	string s6("hello world", 5);
//	/*
//	* 拷贝“hello world”中的前5个字符,
//	* 拷贝到s6中初始化s6
//	*/
//	cout << "构造函数5:" << s6 << endl;
//
//
//	//6:string (size_t n, char c);
//	/*
//	* n:填充的字符个数
//	* c:要填充的字符
//	* 使用n个c字符来填充字符串字符串
//	*/
//	string s7(10, 'x');
//	/*
//	* 使用10个‘x’来填充s7
//	*/
//	cout << "构造函数6:" << s7 << endl;
//
//
//	//7:template <class InputIterator> string (InputIterator first, InputIterator last);
//	/*
//	* 这个string类构造函数因为涉及迭代器的内容,
//	* 所以等了解了迭代器再来了解该类构造函数
//	*/
//
//
//
//
//
//	//string类的默认成员函数 -- 析构函数:
//	/*
//	* string类的析构函数:
//	* string字符串类为了支持扩容,
//	* 其字符数组是动态开辟的,
//	* 动态开辟的空间使用后要进行释放,
//	* 其释放工作就是由析构函数负责的,
//	* 而析构函数一般是自动调用的
//	*/
//	
//
//	//string类的默认成员函数 -- 赋值“=”运算符重载函数:
//	//string::operator= (string类赋值“=”运算符重载函数)
//	/*
//	* 第1种: string& operator= (const string& str);
//	* 第2种: string& operator= (const char* s);
//	* 第3种: string& operator= (char c);
//	* 
//	* 第1种是支持string字符串类对象进行赋值;
//	* 第2种是支持字符串(直接写出字符串)进行赋值;
//	* 第3种是支持单个字符进行赋值
//	*/
//	//string类第1种赋值方法:
//	s1 = s2; //string字符串类对象进行赋值
//	cout << "赋值=运算符重载函数1:" << s1 << endl;
//
//	//string类第2种赋值方法:
//	s1 = "world"; //字符串(直接写出字符串)进行赋值
//	cout << "赋值=运算符重载函数2:" << s1 << endl;
//
//	//string类第3种赋值方法:
//	s1 = 'x'; //单个字符进行赋值
//	cout << "赋值=运算符重载函数3:" << s1 << endl;
//	
//	
//	return 0;
//}


namespace ggdpz
//防止和std中的string命名冲突:
{
	//string类(自己的):
	class string
	{
	private: //私有成员变量:

		char* _str; //字符数组(字符串)指针
		size_t _size; //字符数组大小(长度)
		size_t _capacity; //字符数组容量

		/*
		* 可以简单想象string类的私有成员变量
		* 就是这几个
		*/
	};
}


//string类的遍历和访问:
int main()
{
	string s1("hello world");
	/*
	* string本质是个字符数组,
	* 只不过通过类封装在一起,
	* 如果想要遍历string字符串的话有两种方法:
	* 
	* 遍历的第1种方法:下标 + []
	* 我们访问数组的时候会使用到方括号“[]”,
	* string的底层是数组实现的,所以会对“[]”进行重载,
	* 即operator[],使用string类的operator[]后,
	* 就可以像访问数组一样访问字符串(字符数组)了
	*/

	//string类的成员函数 -- 下标“[]”运算符重载函数:
	//string::operator[]
	/*
	* 第1种: char& operator[] (size_t pos);
	* 第2种: const char& operator[] (size_t pos) const;
	* 
	* pos:访问string字符串对象pos下标的
	* char&:访问pos下标字符后返回该字符的引用(“别名”),
	*		 如果是普通对象则可以修改该字符
	* 
	* 第2种是第1种的重载版本
	*/

	//要获取string类对象的长度有两种方法:
	//第一种方法:size()
	cout << s1.size() << endl; 
	//第二种方法:length()
	cout << s1.length() << endl; 
	/*
	* size() 和 length() 都是返回字符串对象s1的长度,
	* 至于同个功能取两个名字,是因为历史发展的关系,
	* 对于字符串,长度其实使用length()会更合理,
	* 但由于string产生得比STL早,STL出来前string只有length(),
	* 当STL出来后,对像set(树)这种数据结构length(长度)就不太合适了,
	* STL设置接口的时候又需要一定的统一性,length又不能统一使用,
	* 所以又设置了size(大小),所以string类中又有了size(),
	* 之后计算string类时使用size()即可,按照STL的标准来
	* 
	* size() 和 length() 计算string类时不会计算"\0"
	*/
	
	//第一种遍历方法:使用for循环遍历字符串:
	for (size_t i = 0; i < s1.size(); i++)
		//s1.size() 就可以返回string类对象s1的长度
	{
		/*
		* 第1种: char& operator[] (size_t pos);
		*/
		//遍历打印string类对象s1的字符:
		cout << s1[i] << " ";
		//cout << s1.operator[](i) << " "; //等于上面的代码
		/*
		* 这里遍历string类时使用了下标运算符"[]",
		* 让string类可以像遍历数组一样被遍历,
		* 实际string字符串类对象s1调用了下标“[]”运算符重载函数,
		* s1[i] 即 s1.operator[](i)
		*/
	}
	cout << endl; //换行


	/*
	* 使用下标符[]可以读数据,
	* 还可以用它来写数据,跟数组类似,
	* 因为operator[]调用后返回的是char&,
	* 是一个引用,所以字符串使用[]可以直接写(修改)数据
	*/
	s1[0] = 'x';
	cout << s1 << endl;


	//逆置string字符串:
	int begin = 0; //字符串左边界(下标)
	int end = s1.size() - 1; //字符串右边界(下标)
	/*
	* 右边界即字符串最后一个字符下标,
	* 使用size()获得字符串长度,
	* 字符串长度 - 1,即最后一个字符下标
	*/

	while (begin < end) 
		/*
		* begin < end,
		* 说明字符串中还有字符能够逆置
		*/
	{
		//1、通过创建临时变量实现两值交换:
		/*
		* //创建临时变量存储左边界:
		* char tmp = s1[begin];
		*
		* //将右边界字符赋给左边界字符:
		* s1[begin] = s1[end];
		*
		* //将左边界字符赋给右边界字符:
		* s1[end] = tmp;
		*/

		//2、通过C++自带的swap交换函数实现两值交换:
		swap(s1[begin], s1[end]);
		/*
		* C++自带交换函数swap,
		* 因为C++中有了函数模板,
		* 所以不用考虑实际类型的问题,
		* 从而实现了通用的swap交换函数,
		* 使用swap函数需要包含<utility>头文件,
		* 但是这里该头文件间接包含了,
		* 所以不用再显式写出来
		*/

		//进行迭代:
		++begin; //调整左边界
		--end; //调整右边界
	}

	cout << s1 << endl;



	//第二种遍历方法:使用迭代器iterator遍历字符串:
	string::iterator it = s1.begin();
	/*
	* iterator定义在类域中,但它不是内部类,是一个类型,
	* 现在还不熟悉迭代器,它的用法类似指针,
	* 但迭代器不一定是指针
	* 
	* it可以理解成指向字符串(字符数组)首字符的指针,
	* begin() 和 end() 迭代器区间是“左闭右开”的,
	* begin()可以理解成指向字符串首字符的指针,
	* end()可以理解成指向最后一个有效字符的下一位字符('/0')的指针,
	*/
	while (it != s1.end())
	{
		*it += 1; //通过迭代器也可以写(修改)数据
		/*
		* it一开始指向字符串首字符,
		* *it解引用指针后就可以修改该位置的字符了,
		*/
		cout << *it << " "; //打印当前it指针的字符
		++it; //调整it指针位置

		/*
		* it实际可能是指针,也可能不是指针,
		* 可以发现iterator迭代器实际运作方式,
		* 就像是用指针的方式进行字符串遍历访问和修改
		*/

		/*
		* 下标运算符[],只能底层有一定连续的情况下使用,
		* 所以不是所有容器都能够支持。
		* 真正访问容器最方便主流的就是迭代器
		* (链式结构、树形、哈希结构 只能使用迭代器)
		* 
		* 而且各类容器调用迭代器的方式都是相同,
		* 会调用一个容器的迭代器,
		* 其它容器的迭代器也就会使用了
		*/
	}

	cout << endl;


	//对于字符串的逆置,在C++算法中也有,
	//直接调用即可:
	reverse(s1.begin(), s1.end());
	/*
	* 该算法名叫reverse,
	* 使用时传 开始 和 结束 的位置即可,
	* 无论什么容器,只要传迭代器区间给reverse,
	* 就可以实现逆置
	* 
	* 所以迭代器还可以配合算法使用(nb)
	* C++算法也是泛型编程,
	* 不是针对某个容器的迭代器实现的,
	* 函数模板,针对各个容器的迭代器实现
	*/
	cout << s1 << endl;

	return 0;
}


//迭代器:
int main()
{
	//vector类:
	vector<int> v;

	//尾插入数据:
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);

	//vector可以使用类遍历,
	//也可以使用迭代器遍历:
	vector<int>::iterator vit = v.begin();
	/*
	* begin() 会获取第一个位置的迭代器(左闭),
	* 迭代器的区间都是左闭右开的,
	* end() 会获取最后一个数据的下一个位置(右开)
	*/

	while (vit != v.end())
		//begin() 还未到 end() :
	{
		//解引用vit获取当前位置数据:
		cout << *vit << " ";
		++vit; //调整指针
	}

	cout << endl; //换行
	
	//reverse 也可以实现vector的逆置:
	reverse(v.begin(), v.end());



	//list类:
	list<double> lt;

	//尾插入数据:
	lt.push_back(1.1);
	lt.push_back(2.1);
	lt.push_back(3.1);
	lt.push_back(4.1);

	//使用迭代器遍历list:
	list<double>::iterator lit = lt.begin();

	while (lit != lt.end())
		//begin() 还未到 end() :
	{
		//解引用vit获取当前位置数据:
		cout << *lit << " ";
		++lit; //调整指针
	}

	cout << endl; //换行

	//reverse 也可以实现list的逆置:
	reverse(lt.begin(), lt.end());

	return 0;
}



int main()
{
	/*
	* 第2种: const char& operator[] (size_t pos) const;
	* 这个重载构造函数的隐藏this指针是用const修饰的,
	* 该函数主要是为了解决参数匹配的问题
	*/
	string s1{ "hello world" }; //非const对象
	const string s2{ "hello world" }; //const对象
	s1[0] = 'x'; //非const对象s1调用"[]"
	s2[1] = 'x'; //const对象s2调用"[]"
	/*
	* 非const对象s1调用的"[]"运算符重载函数:
	* 第1种: char& operator[] (size_t pos);
	* 
	* const对象s2调用的"[]"运算符重载函数:
	* 第2种: const char& operator[] (size_t pos) const;
	* (const:只读,不能修改)
	* 
	* s1也可以调用“第2种”,非const调用const,
	* “可读可写” 变成 “只读”,访问权限缩小是允许的,
	* 虽然都可以调用“第2种”,但是“第2种”的返回值是const char&,
	* 非const对象调用后返回“只读”的引用(别名)就不合适,
	* 所以还需要实现“第1种” 
	*/

	/*
	* 对于const的string对象s2,因为“只读”,
	* 所以不能像s1非const对象那样使用迭代器:
	*/
	//string::iterator it = s2.begin(); //编译错误

	//应该是const_iterator(“只读”)而不是iterator(“可读可写”):
	string::const_iterator it = s2.begin();
	while (it != s2.end())
	{
		//*it += 1; //const对象数据无法被修改
		cout << *it << " ";
		++it; //迭代器it本身是可以修改的
	}
	cout << endl;
	/*
	* string类的迭代器中的 begin / end,
	* 其实现时也有两个版本:
	* 第1种:iterator begin();
	* 第2种:const_iterator begin() const;
	* 
	* 其中“第2种”const版本中,
	* 它是名字为:const_iterator,
	* 而不是在iterator前直接加const修饰,
	* 
	* 
	* 
	* const_iterator it 和 const iterator it:
	* 
	* const_iterator it 本质是修饰迭代器指向的数据,
	* 即 *it 不能被修改
	* 
	* const iterator it 修饰的是迭代器本身,
	* 迭代器本身不能被修改,即 it 不能被修改,
	* 要进行遍历的话,得调整it(it++),
	* 所以不能直接在iterator前加const进行修饰
	*/



	//容器都能支持范围for循环:
	for (auto e : s1)
		//依次取容器对象s1放入e中进行循环遍历:
	{
		cout << e << " ";
	}
	cout << endl;


	return 0;
}



一般在传参才会用到const对象:
(让对象在函数中使用时不会被改变)
//void func(const string& s)
//{
//	//第二种:const对象版本:
//	string::const_reverse_iterator it = s.rbegin();
//	//这里反向迭代器的const版本的类型很长,所以可以用auto进行省略:
//	auto it = s.rbegin();
//	
//	//进行反向遍历:
//	while (it != s.rend())
//	{
//		//"只读"
//		//*it1 = 'x'; //报错,无法‘写’
//
//		//打印当前字符串s1中it指针指向的字符:
//		cout << *it << " ";
//		//调整it指针:
//		++it;
//	}
//	cout << endl; //换行
//}
//
//
//int main()
//{
//	//string类对象:
//	string s1("hello world");
//	/*
//	* string类对象的三种遍历方式:
//	* 1、下标 + [] :只适用于底层连续的容器
//	* 2、迭代器 :yyds,能配合算法使用
//	* 3、范围for :看似好用,实际是依靠迭代器实现的
//	*/
//
//	/*
//	* string的反向迭代器:std::string::rbegin
//	* 
//	* 第一种:reverse_iterator rbegin();
//	* 第二种:const_reverse_iterator rbegin() const;
//	* (rend 和 rbegin 类似)
//	* 
//	* 对于没有下标的情况,如链表,如果要进行倒着遍历,
//	* 就可以使用其反向迭代器,掌握了迭代器,
//	* 就掌握了所有容器的遍历、访问、修改
//	*/
//	  
//	//第一种:非const版本:
//	string::reverse_iterator it1 = s1.rbegin();
//	//进行反向遍历:
//	while (it1 != s1.rend())
//	{
//		//"可读可写"
//		//*it1 = 'x'; //‘写’
//		
//		//打印当前字符串s1中it指针指向的字符:
//		cout << *it1 << " ";
//		//调整it指针:
//		++it1;
//	}
//	cout << endl; //换行
//
//	/*
//	* 打印:"d l r o w  o l l e h",
//	* 即实现了s1:"h e l l o  w o r l d"的反向遍历
//	*/
//
//
//	//第二种:const对象版本:
//	func(s1);
//	/*
//	* func函数接收const对象,
//	* s1不是const对象,传过去后权限缩小,
//	* “可读可写” 变成 “只读”
//	*/
//
//	return 0;
//}



//int main()
//{
//	//std::string::max_size
//	//(返回字符串可以达到的最大长度)
//
//	//无参字符串对象:
//	string s1;
//
//	//有参字符串对象:
//	string s2("hello world");
//
//	cout << s1.max_size() << endl;
//	cout << s2.max_size() << endl;
//	/*
//	* 无论是s1的无参,还是s2的有参,
//	* 打印两者的“max_size()”时,
//	* (32位系统)都是“214783647”(2^31),
//	* 即整型最大值的一半,
//	* 也就是说此时这两个字符串能达到的最大长度为2^31,
//	* 这是在VS2022上,不同编译器可能不同,
//	* 不同系统也不同(x64/x86),
//	* 实际也不一定就开了2^31个字符的空间
//	*/
//
//
//
//	//std::string::reserve
//	/*
//	* (为字符串预留空间,可以进行扩容操作)
//	* 注意和revserve进行区分,
//	* reserve:保留
//	* reverse:反转、逆置
//	*/
//	s1.reserve(s1.max_size());
//	//保留max_size个空间
//
//	//std::string::capacity
//	//(实际能够存储的有效字符个数)
//	cout << s1.capacity() << endl; //“15”
//	cout << s2.capacity() << endl; //“15”
//	/*
//	* string s1;
//	* string s2("hello world");
//	*
//	* s1 和 s2 的capacity打印时都是“15”,
//	* 即实际可存储的有效字符个数为15,
//	* 但实际是有16个空间的,有一个空间给了"\0",
//	* "\0"不算有效字符,而是标识字符,
//	* 所以capacity打印时为“15”
//	*/
//
//
//
//	//std::string::resize
//	//(将有效字符的个数分割成n个,多出的空间用字符c填充)
//	/*
//	* reserve:只影响容量,不会影响数据;
//	* resize:即影响容量,也影响数据
//	* 
//	* resize有两种实现:
//	* 1、void resize(size_t n);
//	* 2、void resize(size_t n, char c);
//	* 
//	* resize的使用分三种情况:
//	*/
//
//	string s1("hello world");
//	//起始字符串大小(长度)-- 11
//	cout << s1.size() << endl;
//	//起始容量 -- 15
//	cout << s1.capacity() << endl; 
//
//	//第一种情况:resize分割数 > capacity
//	//	(即影响容量,又影响数据)
//	//size为11,capacity为15,分割数为100:
//	s1.resize(100); 
//	
//	//分割后字符串大小 -- 100
//	cout << s1.size() << endl; 
//	//分割后容量 -- 111
//	cout << s1.capacity() << endl; 
//	/*
//	* 当 resize分割数 > capacity 时,
//	* 分割后capacity容量会增容到至少比分割数大,
//	*(这里 capacity--15 就变成至少比 分割数--100 大的111)
//	* 
//	* 而字符串数据 size 从15变成了100,
//	* 那其余“85”的数据是什么数据呢?
//	* 
//	* 这里的resize是第一种实现:void resize(size_t n);
//	* 使用resize这种实现,且 resize分割数 > capacity 时,
//	* 多出的数据就会用 空字符(初识字符)--'/0' 插入,
//	* 这里的'/0'就不是标识字符了,而是有效字符了,
//	* '/0'是哪种字符,取决于'/0'是否在size范围中,
//	* 这里增容后,size从15扩大到100并用'/0'插入充当多余数据,
//	* 此时“数据'/0'”就不是结束符而是有效数据了
//	* 
//	* 如果是第二种实现:void resize(size_t n, char c);
//	* 相同情况下,多出的数据就会用传过来的 字符c 插入
//	* 
//	* resize分割数 > capacity ---- “扩容 + 尾插”
//	*/
//
//
//	string s2("hello world");
//	//起始字符串大小(长度)-- 11
//	cout << s2.size() << endl;
//	//起始容量 -- 15
//	cout << s2.capacity() << endl;
//
//	//第二种情况:size < n < capacity
//	//(只会改变字符串大小size)
//	//size为11,capacity为15,分割数n为12:
//	s2.resize(12);
//
//	//分割后字符串大小 -- 12
//	cout << s2.size() << endl;
//	//分割后容量 -- 15
//	cout << s2.capacity() << endl;
//	/*
//	* 在VS中,reserve不会缩容,resize也不会缩容,
//	* 所以容量还是15,不会改变,
//	* 字符串数据size还是和第一种情况类似,
//	* size < 分割数n,数据size就增加到b,
//	* 并用'/0'或'字符c'插入充当多余数据,
//	*(插入的数据取决于resize是哪种实现)
//	* 
//	*(g++ 和 VS 的resize在这种情况下是一样的)
//	* size < n < capacity ---- “尾插(容量足够)”
//	*/
//
//
//	string s3("hello world");
//	//起始字符串大小(长度)-- 11
//	cout << s3.size() << endl;
//	//起始容量 -- 15
//	cout << s3.capacity() << endl;
//
//	//第三种情况:分割数n < size
//	//(只会改变字符串大小size)
//	//size为11,capacity为15,分割数n为5:
//	s3.resize(5);
//
//	//分割后字符串大小 -- 5
//	cout << s3.size() << endl;
//	//分割后容量 -- 15
//	cout << s3.capacity() << endl;
//	/*
//	* 和第二种情况一样,
//	* 因为capacity容量足够,
//	* 所以不会改变空间大小,
//	* 只对数据进行分割
//	*(g++中和VS也是一样的)
//	* 
//	* 分割数n < size ---- “删除数据,保留分割数n个”
//	*/
//
//	/*
//	* 总结:
//	* resize一定会对数据大小size进行操作,
//	* 对容量capacity可能会进行增容操作
//	*(g++ 和 VS 两个主流平台中)
//	* 在第一种情况中可以 增加数据,
//	* 在第三种情况中可以 删除数据
//	* 所以resize的作用就是:
//	* 1、插入数据(如果空间不够还会扩容)
//	* 2、删除数据
//	* 更多场景下是用于开空间并初始化
//	*/
//}

//int main()
//{
//	//reserve 和 capacity 的使用:
//
//	//无参字符串对象:
//	string s1;
//	//有参字符串对象:
//	string s2("hello world");
//
//	//使用reserve进行空间预留:
//	s1.reserve(500);
//	/*
//	* 如果我们知道大概需要多少空间,
//	* 则可以使用reserve提前开好空间,
//	* 不需要进行后面的扩容操作,
//	* 打印结果:"15、511"
//	* 容量直接一次性扩容到足够存储500个有效字符
//	* (实际512个空间)
//	*/
//
//	//通过capacity检车string的扩容机制:
//	size_t old = s1.capacity(); //s1此时容量
//	cout << old << endl; 
//
//	for (size_t i = 0; i < 100; i++)
//	{
//		//循环依次,尾插一个字符:
//		s1.push_back('x');
//
//		if (old != s1.capacity())
//			/*
//			* 如果 old 和当前容量不同,
//			* 说明old容量被扩容了
//			*/
//		{
//			//打印扩容后容量:
//			cout << s1.capacity() << endl;
//			//更新old容量:
//			old = s1.capacity();
//		}
//	}
//	/*
//	* 如果没有使用reserve进行空间预留,则会进行扩容操作:
//	* 
//	* 打印结果:“15、31、47、70、105”
//	* 容量从15变成31在变成47……
//	* 从整体来说,是按当前容量的1.5倍进行扩容
//	* 
//	* 不同编译器容量和扩容操作不同:
//	* VS -- "15、31、47、70、105" -- 1.5倍扩容
//	* gcc -- "0、1、2、4、8、16……" -- 2倍扩容
//	*/
//
//	/*
//	* reserve在VS上只会增容;
//	* 
//	* 在g++上除了增容,还可以缩容,
//	* 但缩容不会影响数据,只会影响空间,
//	* 即最多缩容到现存数据大小的空间
//	*(缩容不会删除数据,最小缩到size)
//	*/
//}


int main()
{
	
	return 0;
}

Guess you like

Origin blog.csdn.net/weixin_63176266/article/details/134916273