首页 > 编程语言 >C++ STL string初探:string类剖析

C++ STL string初探:string类剖析

时间:2023-06-01 12:36:17浏览次数:40  
标签:字符 string STL s1 C++ int 字符串 cout


一、string的基本概念

1.1string是管理字符数组的类

常见的初始化使用场景:无参构造和拷贝构造

string s1; //无参构造
string s2("hello world");  //有参构造

对string类的总结:

  1. string是表示字符串的字符串类
  2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。

二、string类模板

2.1重要的运算符重载

2.1.1 string::size

C++ STL string初探:string类剖析_标准模板库

2.1.2 string::operator[]

C++ STL string初探:string类剖析_标准模板库_02

string::operator[]底层原理是在堆上开一个字符数组,在字符数组里面存。

这两个的结合可以解决string类字符串的单个遍历,像数组一样进行遍历。

for (int i = 0; i < s1.size(); i++)
	{
		cout << s1[i] << endl;
	}

我们发现虽然底层在字符数组里面存,但是在打印的时候是\0不会显示打印出来?

for(int i=0;i<s2.size()+1;i++)
{
  cout<<s2[i]<<" ";
}

C++ STL string初探:string类剖析_STL_03

C++ STL string初探:string类剖析_string_04

通过编写一段上面的代码发现是还是没有显示打印出来,再监视s2发现当i=11时,寄存器临时变量没有数据进行输出打印。

2.1.3 比较思考

那么string这类的这个字符串和char a[]这些字符数组有没有区别?

比如:s1("hello world");     char a[]="hello world"

差别在哪儿:底层差别 s1本质是一个类调用的string::operator[],a[]本质是数组的首地址也就是一个解引用+地址。 



2.2 迭代器

理解成像指针一样的东西。 

如下:

string::iterator it = s1.begin();

iterator是标准库类模板的关键字,需要自己指定限定符,it相当于是s1这个string类字符串的第一个字符。

常见的使用

#include<iostream>
using namespace std;
int main()
{
   string s1("hello world");
   //使用迭代器的对s1的遍历
   string::iterator it=s1.begin();
   while(it!=s1.end())
   {
      cout<<*it<<endl;
      it++;
   }
   
   //另外一个重要的c++11 可以用的遍历方法
   for(auto i:s1)   //这个的auto其实是自动识别的迭代器类型 <=> string:iterator i:s1
   {
      cout<<i<<" ";
   }
  
 return 0; 
}

那么迭代器可以遍历string的字符串,那么能修改嘛?

标准库的字符串容器std::string是可以进行修改的。

for (auto i : s1)
    {
        i = 'x';
        cout << i << " ";
    }

C++ STL string初探:string类剖析_c++_05

三、string类的常用接口

3.1string类对象的常见构造

C++ STL string初探:string类剖析_标准模板库_06

1.string() ----->调用的构造函数初始化为空字符串

2.string(const string& str) ----->调用的拷贝构造函数

3.string(const string& str,size_t pos,size_t len=npos) ---->将从str这个字符串的pos位置后len个长度进行拷贝构造函数

4.string(const char* s)    ----->用一个字符数组(c-string)来构造对象

5.string(const char* s,size_t n)  ---->选取s字符数组前n个进行拷贝构造

6.string(size_t n,char c)   ---->将string对象初始化为n个字符c

三个重点掌握的string类构造运行实例

#include<iostream>
using namespace std;
int main()
{
	string s1;
  cout<<s1<<endl;
  const char a[]="hello world";   //const可加可不加 不加的话就是权限缩小
  string s2(a);
	cout<<s2<<endl;
  
  string s3(s2);
  cout<<s3<<endl;

	return 0;
}

C++ STL string初探:string类剖析_string_07

3.2string类对象的容量操作

3.2.1 size

功能:返回字符串有效字符长度

string s1("hello world");
cout<<s1.size();
//输出结果为10


3.2.2 length

功能:返回字符串有效字符长度

string s1("hello world");
cout<<s1.length();
//输出结果为10

size()和length()的底层实现是一样的,然后实现的功能也一样,觉得有点冗余,但是size()这个设置是为了与其他标准库保持一样的函数接口设置的。


3.2.3 capacity

功能:返回空间总大小(换句话说,就是返回开辟的空间大小)

string s1("hello world");
cout<<s1.capacity();
//输出15

为什么是输出15呢,我们知道string实际也是顺序表,顺序存储结构,那么也就是扩容的路线。vs下和linux下的扩容策略不一样,vs下是1.5倍。


3.2.4 empty

功能:检测字符串释放为空串,是返回true,否则返回false。

int main()
{
	string s1;

	if (s1.empty())
	{
		cout << s1.empty() << endl;
	}
	string s2("hsq");
	if (!s2.empty())
	{
		cout << s2.empty() << endl;
	}
	return 0;
}

C++ STL string初探:string类剖析_c++_08

3.2.5 clear

C++ STL string初探:string类剖析_string_09

功能:清空有效字符

string s2("hsq");
	s2.clear();
	cout << s2 << endl;
//输出为空

思考:我把有效字符清空了,那么我的空间会有影响?

测试:

string s2("hsq");
	cout << "clear前容量" << s2.capacity() << endl;
	s2.clear();
	cout << s2 << endl;
	cout << "clear后容量" << s2.capacity() << endl;

C++ STL string初探:string类剖析_标准模板库_10

可见,clear只进行有效字符的清理工作,对string对象字符串的开辟的内存空间是不影响的。


3.2.6 reserve 

C++ STL string初探:string类剖析_string_11

功能:为字符串预留空间(适用于已知需要多少空间的情况,减少扩容操作)

int main()
{
	string s1;
	cout << s1.capacity() << endl;
	s1.reserve(100);
	cout<< s1.capacity() << endl;
  
	return 0;
}

C++ STL string初探:string类剖析_c++_12

可见,reserve函数可以提前预留空间,改变的底层的内存空间。

那么预留空间小于当前string对象的空间会发生什么情况呢?

测试:

int main()
{
	string s1("hello world");
	cout << s1.capacity() << endl;
	s1.reserve(2);
	cout << s1.capacity() << endl;


	return 0;
}

C++ STL string初探:string类剖析_string_13

当预留空间小于string对象的内存空间,是不会进行预留空间的操作,也不会缩减空间,但是在其他环境下可能会进行缩容。

3.2.7 resize

C++ STL string初探:string类剖析_string_14

功能:将有效字符的个数改成n个,多出的空间用字符c填充

int main()
{
	string s1("hello world");
	cout << "原始的有效字符个数" << s1.size() << endl;
	cout <<"原始的内存空间容量" << s1.capacity() << endl;
	//把有效字符个数改成100个 
	s1.resize(100);
	cout <<"字符串" << s1 << endl;
	cout << "有效字符个数" << s1.size() << endl;
	cout << "内存空间容量" << s1.capacity() << endl;
	cout << "-------------------------------" << endl;
	//把有效字符个数改成100个 未填充的填充为'a'
	string s2("hello world");
	cout << "原始的有效字符个数" << s2.size() << endl;
	cout << "原始的内存空间容量" << s2.capacity() << endl;
	s2.resize(100,'a');
	cout << "字符串" << s2 << endl;
	cout << "有效字符个数" << s2.size() << endl;
	cout << "内存空间容量" << s2.capacity() << endl;
	cout << "-------------------------------" << endl;

	return 0;
}

C++ STL string初探:string类剖析_STL_15

C++ STL string初探:string类剖析_c++_16

通过两次相同函数的重载调用发现,当进行了resize那么会改变size大小是进行增大size大小的话,capacity相应的按照自己扩容的策略进行扩容;当是进行size减少(有效字符数据可能丢失),那么底层的capacity容量不变。当传入第二个字符参数‘a’,那么字符串中未被填充的就会被填充为a;如果不填,那么就是用0来天聪多出的元素空间。

3.2.8 resize和reserve的对比区别:

#include<iostream>
using namespace std;
int main()
{

	string s1("hello world");

	s1.reserve(100);  //单纯的开空间 改变的capacity
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;


	s1.resize(100);   //开空间+填值
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;



	return 0;
}

C++ STL string初探:string类剖析_string_17

reserve:单纯的开空间 改变的capacity,当是要缩小的时候是不改变capacity和size的。

resize:开空间+填值(默认为\0) 或者 删除数据 不缩容(空间大小还在)

3.3string类对象的访问及遍历操作

3.3.1operator[] 

C++ STL string初探:string类剖析_string_18

功能:返回pos位置的字符,const string类对象调用

这里在上面进行了分析,是string重要的运算符重载,使得string对象的字符串,能像数组一样进行直接访问和遍历,极大的解决很多操作的难点

int main()
{
	string s1("hello world");

	for (int i = 0; i < s1.size(); i++)
	{
		cout << s1[i] << " ";
	}

	return 0;
}

C++ STL string初探:string类剖析_string_19

 3.3.2 begin+end /rbegin+rend

begin+end :

功能:begin获取一个字符的迭代器,+end获取最后一个字符下一个位置的迭代器。

rbegin+rend:

功能:begin获取一个字符的迭代器,+end获取最后一个字符下一个位置的迭代器。

int main()
{
	string s1("hello world");
	
	auto it = s1.begin();
	//访问
	cout << *it << endl;
	//遍历+访问
	while (it != s1.end())
	{
		cout << *it << " ";
		it++;
	}

	return 0;
}


C++ STL string初探:string类剖析_c++_20


s1.begin() 是指向s1这个string对象的第一个字符。可以差不多理解为c语言的指针,但是从根本上不是指针。


3.3.3 范围for

c++11支持的新的遍历方式

实现的底层其实还是用的迭代器。

for(auto i:x)
{
  cout<<i<<" ";
  
}

3.4 string类对象的修改操作

3.4.1 push_back(尾插)

push_back这个在标准模板库里面是不陌生的,很多都有这个接口函数。

功能:在字符串后尾插字符c

int main()
{
	string s1("hello world");
	s1.push_back('a');
	cout << s1 << endl;

	return 0;
}

C++ STL string初探:string类剖析_string_21

3.4.2 append

相比push_back这个是在字符串后面追加一个字符串

int main()
{
	string s1("hello world");
	s1.append("hsq");
	cout << s1 << endl;

	return 0;
}

C++ STL string初探:string类剖析_c++_22

3.4.3 operator+=

功能:在字符串后面追加字符串strC++ STL string初探:string类剖析_string_23

这里的运算符重载,简直就是完美的设计,用一个运算符重载+函数重载解决了前面push_back和append所做的事情。

功能:在字符串后面追加字符串str

int main()
{
	string s1("hello world");

	s1 += 'a';  //等价于 s1.push_back('a');
	cout << s1 << endl;
	s1 += "hsq";  //等价于 s1.append("hsq");
	cout << s1 << endl;

	return 0;
}

C++ STL string初探:string类剖析_c++_24

3.4.4 c_str

功能:返回C格式字符串

在我看来string的内置成员就是用c_str来存储字符数组。

int main()
{
	string s1("hello world");
	const char* arr = s1.c_str();  
	for (int i = 0; i < s1.size(); i++)
	{
		cout << arr[i] << " ";
	}

	return 0;
}

注意:用一个字符数组接收的时候要加const,不加就是权限扩大,那就会报错。

C++ STL string初探:string类剖析_STL_25

3.4.5find/rfind

C++ STL string初探:string类剖析_string_26

C++ STL string初探:string类剖析_c++_27

find功能:从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置

rfind功能:从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置

有时候,在做oj题目的时候,find可以解决很多棘手的问题,哈哈哈我只能说find大法好。find可以解决需要通过遍历来找到对应的字符。

 用find的简单查找方法:

int main()
{
	string s1("hsq");
	cout << s1.find('q') << endl;
	cout << s1.rfind('q') << endl;

	return 0;
}

复杂的查找方法:

for (int i=0;i<s1.size();i++)
	{
		if (s1[i] == 'q')
		{
			cout << i << endl;
		}
	}

3.4.6 substr

功能:在str中从pos位置开始,截取n个字符,然后将其返回。

如果没有第二个位置的参数,那么默认截取是从pos位置开始到结束

int main()
{
	string s1("hello world");
	cout<<s1.substr(2)<<endl;  //从2这个位置开始往后到结束全部截取并返回

	cout<<s1.substr(1, 2)<<endl;  //从1这个位置后两位进行截取并返回

	return 0;
}

C++ STL string初探:string类剖析_c++_28



3.5string非成员函数

3.5.1 operator+ 

C++ STL string初探:string类剖析_c++_29

功能:运算符重载,加法运算,尽量少用,传值返回,导致深拷贝效率低。

#include<iostream>
using namespace std;
int main()
{
	string a("hsq");
	cout << a + "hsq" << endl;    // +字符串
	cout << a + 'a' << endl;      // +字符
	string b("hello world");
	cout << (a + b) << endl;      //+string对象


	return 0;
}

C++ STL string初探:string类剖析_string_30

3.5.2 operator>>

C++ STL string初探:string类剖析_STL_31

功能:输入运算符重载

int  main()
{
	string a;
	cin >> a;  //读到空格停止

	cout << a << endl;

	return 0;
}

3.5.3 operator<<

功能:输出运算符重载

int  main()
{
	string a;
	cin >> a;  //读到空格停止

	cout << a << endl;


	return 0;
}


3.5.4 getline

C++ STL string初探:string类剖析_STL_32

功能:获取一行字符串

为什么有getline?用cin不就好了?

cin特性是当读到空格就会停止,那么当需要包括空格之外的一行都要读,那么就得用getline了。

#include<iostream>
#include<string>  //注意这个要加string这个头文件
using namespace std;

int main()
{
	string s1;
	getline(cin, s1);

	cout << s1 << endl;

	return 0;
}

C++ STL string初探:string类剖析_标准模板库_33


3.5.5 relational operators

功能:比较大小

实际运用的是>、>=、<、<=这些符号,比较正确就是返回1,不正确就是返回0

int main()
{
	string a("123");
	string b("124");
	cout <<( a > b) << endl;

	return 0;
}


C++ STL string初探:string类剖析_string_34







标签:字符,string,STL,s1,C++,int,字符串,cout
From: https://blog.51cto.com/u_15831056/6393194

相关文章

  • c++ const详解
    可以使用const的地方就尽量使用const一般引用的类型必须与其所引用对象的类型一致,但是允许一个常量引用绑定到非常量的对象、字面值,甚至是一个一般表达式doublea=42.0;int&b=a;//编译错误,一般引用类型需要与所引用对象类型一致inta=42;constint&b=a;a=......
  • C++控制台实现串口类似超级终端收发功能
     #include<iostream>#include<windows.h>#include<thread>classSerialPort{public://构造函数,打开串口并设置参数SerialPort(constchar*portName){hSerial=CreateFileA(portName,GENERIC_READ|GENERIC_WRITE,0,N......
  • C++模板从入门到精通:初阶篇
    一、泛型编程1.1什么是泛型编程?泛型编程是一种编程风格,其中算法以尽可能抽象的方式编写,而不依赖于将在其上执行这些算法的数据形式1。泛型编程可以提高代码的重用性、可维护性和类型安全性。泛型编程的基础是模板,模板是创建泛型类或函数的蓝图或公式。平常写的函数与泛型编程的模板......
  • C++中分别使用左值形参和右值形参的拷贝构造和移动构造
    #include<iostream>classData{public:Data(){std::cout<<"EMPTY."<<std::endl;}Data(constData&d){std::cout<<"lvaluecopy."<<std::endl;}Data(constData&&d){std::cout......
  • [C++学习] 整型常量
    C++中二进制以0b开头,如0b00001011(值为11);八进制以0开头,如0013(值为11);十六进制以0x开头,如0x001a(值为26);注意:C++中cout是默认将数据以十进制输出,如果要将数据以八进制,十六进制输出应在前面加oct或hex;如:cout<<oct<<0123<<endl; //输出123cout<<hex<<0x1e<<en......
  • mongocxx c++ 14标准,进行多表联合查询
     #include<mongocxx/client.hpp>#include<mongocxx/instance.hpp>#include<mongocxx/uri.hpp>#include<bsoncxx/builder/stream/document.hpp>#include<bsoncxx/json.hpp>#include<bsoncxx/types.hpp>usingbsoncxx::builder::s......
  • C++指针需要知道的小细节
    一、定义和赋值一个普通对象intival=1024;如上述代码所示,将ival定义为一个int对象,并给予初始值1024。二、指针对象的声明int*pi;1、如上述代码所示,当定义/声明某个特定类型的指针时,要在类型名称后加一个*号。2、在这里,pi是int类型对象的指针。三、指针的初始值int*......
  • C++ 在 cout 中使用关系表达式
    用std::cout输出关系运算表达式时,关系表达式要加括号,否则编译会报错。例如:#include<iostream>intmain(intargc,char**argv){std::cout<<1<2<<std::endl;return0;}在linux中编译后报错内容如下:test.cpp:Infunction'intmain(int,char**)':te......
  • C++基础知识系列-4
    C++基础1C++的struct和class的区别区别1:默认继承的权限。不明确指定的情况下,来自class的继承按照private继承处理,来自struct继承按照public处理区别2:成员的默认访问权限。class成员默认是private权限,struct默认是public2C和C++区别struct上的区别:C中无Protection行为,不能定......
  • c++算法:二分
    算法中,有一种比线性查找算力费得更少的一种算法思想,叫“分治”,今天讲的是分治里的二分查找:借助(low+high)/2公式,找到搜索区域内的中间元素。图1中,搜索区域内中间元素的位置是 ⌊(1+10)/2⌋=5,因此中间元素是27,此元素显然不是要找的目标元素。然后就是缩小范围。 下面就是......