阅读string类文档:类的声明 -成员类型 -成员函数 -迭代器 -容量 -元素访问 -修饰符 -字符串操作 -成员常量 -非成员函数重载

今天,我们开始学习string类,我们将通过它的文档来了解它的内容及操作。
首先我们观察一下string类文档的框架。
图一 string类文档框架

一、string类的声明

  • string类是管理字符的顺序表,字符串是表示字符序列的类。
  • 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
  • 通过图一,我们可以看到**typedef basic_string string; **,string 类是 basic_string 类模板的实例化,string在底层实际是basic_string模板类的别名。
  • 不能操作多字节(在某些编码系统(如UTF-8)中,一个字符可能由多个字节组成。对于这样的字符,如果字符串操作不当,可能会导致字符被错误地分割或者解释。)或者变长字符(字符串是由可变长度的单元组成的,这意味着不同的字符可能占用不同数量的存储空间。在处理这样的字符串时,需要特别注意字符间的边界)的序列。

二、string类的成员类型

image.png
string类的成员类型,我们看一眼了解一下即可。
其中重要的是 iterator(迭代器),我们在后续的接口说明中会学习到。

string类的接口说明(常用的和需要了解的)

在学习string类时的重点是对它的接口学习了解。我们要熟悉它们的功能和操作,同时在观察它们的功能和操作时可以思考,如果让我们模拟实现string类,我们要怎么实现?

三、成员常量

成员常量nposnpos的定义值为-1,因为size_t是一个无符号整数类型,所以它是该类型可能表示的最大值

四、string类对象的常见构造

image.png

(destructor)—— 构造函数
(destructor)—— 析构函数
operator= —— 赋值

(一)构造函数

image.png
先自己根据参数的类型和个数来分析它们的作用是什么。
string类的函数数量众多,我们只学习常用的和需要了解的,在使用的过程中,大家可以时刻查看文档加深记忆。
image.png
这里只介绍四种常用的构造函数。
其中(3)中的size_t len = npos; 是缺省参数,当使用默认值时,npos就是最大值,也就是说我们拷贝的字符串从pos位置开始有多大就拷贝多少。
(5)中就是用C-string中的n个字符来构造string类对象。
(7)InputIterator:输入迭代器。满足“InputIterator”概念的一些类型示例包括C++标准库中的“std::vector<T>::迭代器”、“std::list<T>::迭代器”和“std::string::迭代器”。用户定义的类型也可以通过实现必要的操作和成员函数来满足“InputIterator”概念。

void TestString1()
{
	//创建字符串的操作
	string s0;					//构造空的string类对象
	string s1 = "hello ";		//构造+拷贝构造(隐式类型转换)
	string s2("oooooooooo");	//用C格式字符串构造string类对象s2
	string s3(s2);				//拷贝构造s3

	cout << s1 << endl;
	cout << s0 << endl;
	cout << s2 << endl;
	cout << s3 << endl;
}

image.png

在学习下列知识前,我们先来了解以下,string内部的组成。
char _str;*
size_t _size;
size_t _capacity;
image.png

(二)string类对象的容量操作

image.png
仍然是选择我们常用的函数进行详细说明

函数名称功能说明
sizelength:为了方便模板使用,
新定义的名字,功能与size一致)
返回字符串有效字符长度
capacity返回空间总大小(大小不算’\0’)
reserve为字符串预留空间(请求更改容量(公共成员函数))
resize调整字符串大小,多出的空间用字符c填充
clear清除字符串
empty测试字符串是否为空

详细解释:

1.size(),capacity()和reserve()

void TestString4()
{
	string s0;
	string s1 = "hello world ";
	string s2("oooooooooo");
	string s3(s2);

	cout <<"s1.size():  "<< s1.size() << endl;
	cout << "s1.capacity():  " << s1.capacity() << endl;

	s1.reserve(100);
	cout << "s1.reserve(100):" << endl;
	cout<<"s1.size():  " << s1.size() << endl;
	cout << "s1.capacity():  " << s1.capacity() << endl;

	s1.reserve(10);
	cout << "s1.reserve(10):" << endl;
	cout << "s1.size():  " << s1.size() << endl;
	cout << "s1.capacity():  " << s1.capacity() << endl;
}

image.png
size()和capacity()的功能我们如图所示。
接下来需要主要了解的是:reserve().

reserve(size_t res_arg = 0): 为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间大小时,不会改变容量capacity的大小。
注意到reserve出的空间是大于想要空间的,这说明编译器为我们想要的空间又增加的空间,而reserve出的空间大小主要看编译器内部规定。
我们可以看到reserve预留出的空间100,再调整为10,capacity是不会发生改变的。
reserve的使用提高了效率,在知道我们需要空间大小时,提前开好空间,可以减少频繁扩容(频繁扩容是有代价的)。

2.resize的功能

image.png
注意,在使用resize时,要考虑到n的大小。

resize(size_t n);与resize(size_t n, char c); 都是将字符串中有效字符个数改变到n个。

  1. 当n小于s. size时,有效字符就改为n个,相当于删除了n后的字符,字符内容是不会发生改变,s.size会发生改变,但是s.capacity不会发生改变。
  2. 当n在范围2中,即n>s.size && n<s.capacity 时,**resize(size_t n);**用0来填充,**resize(size_t n, char c);**用字符c来填充多出来的空间。
  3. 当n在范围3中,resize在改变元素个数时,将元素个数增多时会改变底层容量的大小。,s.size会发生改变,s.capacity也会发生改变。

看代码,验证:

void TestString5()
{
	string s0;
	string s1 = "hello world ";
	string s2("Good morning ");
	string s3(s2);

	//1. n < s2.size()
	s1.resize(5);
	cout << "s1.resize(5): " << s1 << endl;
	cout << "s1.size():  " << s1.size() << endl;
	cout << "s1.capacity():  " << s1.capacity() << endl;
	cout << endl;

	//2. n > s2.size && n < s2.capacity 
	s2.reserve(20);
	cout << "s2.size():  " << s2.size() << endl;
	cout << "s2.capacity():  " << s2.capacity() << endl;
	cout << endl;

	s2.resize(15,'!');
	cout << "s2.resize(15): " << s2 << endl;
	cout << "s2.size():  " << s2.size() << endl;
	cout << "s2.capacity():  " << s2.capacity() << endl;
	cout << endl;

	//3. n > s3.capacity 
	s3.reserve(20);
	cout << "s3.size():  " << s3.size() << endl;
	cout << "s3.capacity():  " << s3.capacity() << endl;
	cout << endl;

	s3.resize(125, '!');
	cout << "s3.resize(15): " << s3 << endl;
	cout << "s3.size():  " << s3.size() << endl;
	cout << "s3.capacity():  " << s3.capacity() << endl;
}

image.png

3.clean()和empty()

clean(): 只是将string中有效字符清空,不改变底层空间大小。
empty(): 检测字符串释放为空串,是返回true,否则返回false。

void TestString6()
{
	string s2("Good morning ");

	s2.clear();
	cout << "s2.size():  " << s2.size() << endl;
	cout << "s2.capacity():  " << s2.capacity() << endl;
	
	if (s2.empty())
	{
		cout << "s2为空" << endl;
	}
}

image.png

(三)string类对象的迭代器和元素访问操作

对string类对象的访问及遍历操作。
image.png
image.png

函数名称功能说明
begin/endbegin获取一个字符的迭代器,end获取最后一个字符下一个位置的迭代器
rbegin/rendbegin获取一个字符的反向迭代器,end获取最后一个字符下一个位置的反向迭代器,需要注意,指针的变化任然是++
operator[]返回pos位置的字符,const string类对象调用
范围forC++11支持更简洁的范围for的新遍历方式

1.begin/end —— iterator(迭代器)

image.png
均拥有const修饰的函数和非const的函数。
迭代器的使用:

string::iterator  it = s2.begin();
while (it != s2.end())
{
	cout << *it << " ";
	it++;
}

思考:此处的 while (it != s2.end()) 能否更改为while (it < s2.end())?
答案:可以,但是不建议使用,我们在C++中使用模板时,考虑的是多种类型而并非单独的string类,要考虑代码的通用性。那么为什么此处可以使用呢?
image.png
从上图中我们可以了解到,当物理空间不连续时,指针的位置并不知道是否都在end()前方,用<比较是思考不全面的。

2.rbegin/rend

反向迭代器。
image.png

cout << endl << "反向迭代器s2:   ";
string::reverse_iterator  rit = s2.rbegin();
while (rit != s2.rend())
{
	cout << *rit << " ";
	rit++;
}

3.operator[ ]

返回pos位置的字符,const string类对象调用。越界中止程序。
与at的功能重叠,区别是后者失败时会抛异常,不常使用。
image.png

//operator[]  
cout << endl << "operator[]读,s1:    ";
for (int i = 0; i < s1.size(); i++)
{
	cout << s1[i] << " "; //读
}

cout << endl << "operator[]写,s1:    ";
for (int i = 0; i < s1.size(); i++)
{
	s1[i]++;		//写
}
cout << s1 << " ";

4.范围for

范围for不支持逆序遍历,不支持修改(若要修改字符串,需要auto& 给引用)

cout << endl << "范围for,s2:    ";
for (auto ch : s2)//auto& ch:s2   可以对s2修改?
{
	cout << ch << " ";
	//ch++;   无法实现
}

cout << endl << "范围for,修改s2:    ";
for (auto& ch : s2)//auto& ch:s2   可以对s2修改 注意前置++和后置++ 会对打印结果产生影响
{
	cout << ++ch << " ";
	//cout << ch++ << " ";//对结果没有影响

	/*cout << ch << " ";
	ch++;   //对结果没有影响
	++ch;   //对结果没有影响*/
}

cout << endl << s2 << endl;

5.程序结果展示

void TestString2()
{
	//创建字符串的操作
	string s1 = "hello ";
	string s2("Good morning ");

	cout << "s1:" << s1 << endl;
	cout << "s2:" << s2 << endl;


	//遍历   []   迭代器begin end    范围for
	//
	//operator[]  
	cout << endl << "operator[]读,s1:    ";
	for (int i = 0; i < s1.size(); i++)
	{
		cout << s1[i] << " "; //读
	}

	cout << endl << "operator[]写,s1:    ";
	for (int i = 0; i < s1.size(); i++)
	{
		s1[i]++;		//写
	}
	cout << s1 << " ";


	cout << endl << "迭代器s2:   ";
	string::iterator  it = s2.begin();
	while (it != s2.end())
	{
		cout << *it << " ";
		it++;
	}

	cout << endl << "反向迭代器s2:   ";
	string::reverse_iterator  rit = s2.rbegin();
	while (rit != s2.rend())
	{
		cout << *rit << " ";
		rit++;
	}


	cout << endl << "范围for,s2:    ";
	for (auto ch : s2)//auto& ch:s2   可以对s2修改?
	{
		cout << ch << " ";
		//ch++;   无法实现
	}

	cout << endl << "范围for,修改s2:    ";
	for (auto& ch : s2)//auto& ch:s2   可以对s2修改 注意前置++和后置++ 会对打印结果产生影响
	{
		cout << ++ch << " ";
		//cout << ch++ << " ";

		/*cout << ch << " ";
		ch++;   //对结果没有影响
		++ch;   //对结果没有影响*/
	}

	cout << endl << s2 << endl;
}

image.png

(四)string类对象的修改操作

image.png

函数名称功能说明
operator+=在字符串后追加字符串str
append在字符串后追加一个字符串
push_back在字符串后尾插字符c
pop_back在字符串后删除最后字符c
assign将内容分配给字符串(可以作为赋值使用)
insert插入(字符,字符串)到字符串中
erase从字符串中删除字符
replace替换字符串中的一部分
swap交换字符串值

代码演示:

1.operator+=(常用)

image.png

string s1 = "hello ";
string s2("Good morning");
string s3 = "world";

cout << "s1: " << s1 << endl;
cout << "s2: " << s2 << endl;
cout << "s3: " << s3 << endl;

//附加
s3 += '.';

s1 += s3;
cout << "s1 += s3 : " << s1 << endl;

s2 += "!!!";
cout << "s2 += !!! : " << s2 << endl;

image.png

2.insert/erase

image.png
image.png

//插入
s1.insert(5, "world.");		//注意参数
cout << "s1.insert:  " << s1 << endl;

s1.erase(3);
cout << "s1.erase:  " << s1 << endl;

s1.insert(0, 8, 'x');
cout << "s1.insert:  " << s1 << endl;

s1.erase(5,3);
cout << "s1.erase:  " << s1 << endl;

image.png

3.append/push_back/pop_back

image.png
image.png
image.png

s1.append("world");
cout << "s1.append :  " << s1 << endl;

s1.push_back('.');
cout << "s1.push_back :  " << s1 << endl;

s1.append(s2);
cout << "s1.append :  " << s1 << endl;

s1.pop_back();
cout << "s1.pop_back :  " << s1 << endl;

image.png

4.swap

image.png
image.png

5.replace( 字符串操作中的函数搭配使用也可达到此效果)

image.png

// replacing in a string
#include <iostream>
#include <string>

int main ()
{
  std::string base="this is a test string.";
  std::string str2="n example";
  std::string str3="sample phrase";
  std::string str4="useful.";

  // replace signatures used in the same order as described above:

  // Using positions:                 0123456789*123456789*12345
  std::string str=base;           // "this is a test string."
  str.replace(9,5,str2);          // "this is an example string." (1)
  str.replace(19,6,str3,7,6);     // "this is an example phrase." (2)
  str.replace(8,10,"just a");     // "this is just a phrase."     (3)
  str.replace(8,6,"a shorty",7);  // "this is a short phrase."    (4)
  str.replace(22,1,3,'!');        // "this is a short phrase!!!"  (5)

  // Using iterators:                                               0123456789*123456789*
  str.replace(str.begin(),str.end()-3,str3);                    // "sample phrase!!!"      (1)
  str.replace(str.begin(),str.begin()+6,"replace");             // "replace phrase!!!"     (3)
  str.replace(str.begin()+8,str.begin()+14,"is coolness",7);    // "replace is cool!!!"    (4)
  str.replace(str.begin()+12,str.end()-4,4,'o');                // "replace is cooool!!!"  (5)
  str.replace(str.begin()+11,str.end(),str4.begin(),str4.end());// "replace is useful."    (6)
  std::cout << str << '\n';
  return 0;
}

(五)string类对象的字符串操作

image.png
了解:

// string::find_first_of
#include <iostream>       // std::cout
#include <string>         // std::string
#include <cstddef>        // std::size_t

int main ()
{
  std::string str ("Please, replace the vowels in this sentence by asterisks.");
  std::size_t found = str.find_first_of("aeiou");
  while (found!=std::string::npos)
  {
    str[found]='*';
    found=str.find_first_of("aeiou",found+1);
  }

  std::cout << str << '\n';

  return 0;
}
函数名称功能说明
find_first_of在字符串中查找字符
find_last_of从末尾查找字符串中的字符
find_first_not_of查找字符串中缺少字符
find_last_not_of从末尾查找字符串中不匹配的字符

常用:

函数名称功能说明
c_str返回C格式字符串
find在字符串中查找内容。从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置
rfind查找字符串中内容的最后出现次数。从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置
substr生成子字符串。在str中从pos位置开始,截取n个字符,将其返回

find

image.png

substr

image.png

//find+rfind+substr

//find+rfind+substr
void TestString7()
{
	string s1("legacy.cplusplus.com");
	//size_t i = s1.find('.');
	size_t i = s1.rfind('.');

	string s2 = s1.substr(i);
	cout << s2 << endl;

	//string s3("https://legacy.cplusplus.com/reference/string/string/?kw=string");
	string s3("https://legacy.cplusplus.com/reference/string/string/find/");
	// 协议
	// 域名
	// 资源名

	string sub1, sub2, sub3;
	size_t i1 = s3.find(':');
	if (i1 != string::npos)
		sub1 = s3.substr(0, i1);
	else
		cout << "没有找到协议" << endl;

	size_t i2 = s3.find('/', i1 + 3);
	if (i2 != string::npos)
		sub2 = s3.substr(i1 + 3, i2 - (i1 + 3));
	else
		cout << "没有找到域名" << endl;

	sub3 = s3.substr(i2 + 1);

	cout << sub1 << endl;
	cout << sub2 << endl;
	cout << sub3 << endl;
}

image.png

(六)string类非成员函数重载

image.png

函数名称功能说明
operator+少用,传值返回,深拷贝效率低,连接字符串
operator>>输入运算符重载,从流中提取字符串
operator<<输出运算符重载,将字符串插入流
swap交换两个字符串的值
relational operators字符串的关系运算符(大小比较
getline将行从流获取到字符串(可以读取’ ’ 空格,cin读取不了空格)

relational operators

image.png

//relational operators

// string comparisons
#include <iostream>
#include <vector>

int main ()
{
  std::string foo = "alpha";
  std::string bar = "beta";

  if (foo==bar) std::cout << "foo and bar are equal\n";
  if (foo!=bar) std::cout << "foo and bar are not equal\n";
  if (foo< bar) std::cout << "foo is less than bar\n";
  if (foo> bar) std::cout << "foo is greater than bar\n";
  if (foo<=bar) std::cout << "foo is less than or equal to bar\n";
  if (foo>=bar) std::cout << "foo is greater than or equal to bar\n";

  return 0;
}
  • 37
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值