【C++从0到王者】第九站:String基本介绍及使用

一、String的基本使用

当我们想要使用string库的时候,我们需要先包上头文件string。然后还需要展开命名空间,如果没有展开,就需要指定了,如下所示是string类的一些基本用法,我们可以注意到与普通类的用法还是比较一致的。

#include<iostream>
#include<string>
using namespace std;
int main()
{
	string s1;
	std::string s2;
	std::string name("张三");
	name = "张飞";
	return 0;
}

二、String构造相关的成员函数

在这里,我们就需要去查看文档了。我们使用这个网站来进行查阅cplusplus.com/我们,直接搜索String即可
在这里插入图片描述我们可以注意到下面有这个类的成员函数的使用方法
在这里插入图片描述在string类中,大概有100多个成员函数,我们不可能全部记住,所以我们只需要掌握最常用的即可,剩下的我们需要的时候查文档即可

1.String构造函数

我们进入文档的构造函数部分,我们发下有七个构造函数
在这里插入图片描述
我们可以注意到:

  1. 第一个是无参的默认构造函数
  2. 第二个是拷贝构造
  3. 第四个是用一个c语言的字符串去构造
  4. 第六个是使用使用一个字符,用n个字符去构造

如上四个是比较常用的构造函数。他们的使用如下所示:

int main()
{
	//无参的默认构造
	string s1;
	//用字符串去构造
	string s2("hello world");
	//用n个字符去构造
	string s3(10, '*');
	//拷贝构造
	string s4(s2);

	cout << s1 << endl;
	cout << s2 << endl;
	cout << s3 << endl;
	cout << s4 << endl;

	return 0;
}

如下所示为运行结果,在上面这段代码中,我们可以注意到流插入运算符可以直接运用于string类,这是因为流插入运算符已经在库里面被重载过了。除此之外,我们也有< ,>,==等运算符的重载,这些运算符重载的比较规则与C语言的strcmp是一样的。根据ASCII码来进行比较。但是如果要使用流插入等进行打印的时候,注意优先级
在这里插入图片描述

在这里插入图片描述

我们再来看一些不常用的几个函数:

  1. 第三个构造函数的功能是拷贝构造的一个版本,pos是某个的下标,将对象的pos这个下标开始,将len长度的字符串拷贝构造给一个新对象。我们也可以注意到len有一个缺省参数,这个缺省参数是npos,它的值是-1。但由于它接收的是无符号的数,所以这个-1会变得非常之大。可以认为是如果胜利了len,就从pos位置拷贝构造完在这里插入图片描述在这里插入图片描述这个函数的一个应用就是切割字符串,如下所示,当然下面的方法比较笨拙需要我们手动算位置。我们未来可以来自动搜索位置
    在这里插入图片描述
  2. 第五个函数的作用是拷贝字符数组的前n进行类对象的初始化
  3. 第七个函数是涉及到迭代器的使用,我们在后文在详细了解

2.String析构函数

如下所示是析构函数的的使用说明,事实上析构函数我们是不需要自己去管的。因为它生命周期结束的时候自动调用
在这里插入图片描述

3.operator=运算符重载

在这里插入图片描述如上所示,这个运算符重载有三个函数,他们由于参数不同又构成了函数重载。

  1. 第一个的作用是用一个对象运算符重载赋值给一个对象。
  2. 第二个的作用是用一个字符串去赋值一个对象
  3. 第三个是用一个字符去赋值一个对象

在这里插入图片描述
但是我们其实可以注意到,第二个和第三个其实没有也是可以的。因为编译器又隐式类型转换,它会自动用这个字符串或者字符去构造一个类对象,然后将这个类对象去拷贝构造给一个对象。但是编译器优化后就直接将构造+拷贝构造优化为了构造

4.String增删查改之增

我们创建好一个对象后,我们需要对其进行增删查改等操作。
当我们想要在一个对象后面增加一个字符的时候,我们使用push_back函数
当我们想要增加一个字符串的时候,我们使用append这个函数
在这里插入图片描述在这里插入图片描述这里我们要注意的是,append虽然有很多成员函数,但我们最喜欢最常用追加字符串的那个函数

int main()
{
	string s1("hello");
	s1.push_back(' ');
	s1.append("world");
	cout << s1 << endl;
	return 0;
}

在这里插入图片描述
还需要注意的是,虽然c语言中有strcat这个也是追加字符串的函数,但是使用string的效率更高。而且还会涉及空间不够的问题,而string不涉及空间不够的问题,空间不够直接扩容即可,因为string的本质其实就是一个顺序表,它在类里面的成员变量大致如下

class string
{
public:
private:
	char* _str;
	size_t _size;
	size_t _capacity;
};

虽然push_back和append很好,但是还有一个函数是operator+=,我们更喜欢使用它
在这里插入图片描述它有三个函数重载,它可以加一个类对象,加一个字符串,加一个字符
在这里插入图片描述
事实上operator+=的内部就是复用push_back和append实现的。
但是append并不能完全被替代掉,我们继续观察以下append的函数重载有哪些
在这里插入图片描述我们发现它与构造函数是非常相似的。功能也是类似的

  1. 第一个是追加一个对象
  2. 第二个是将对象第subpos位置开始,后面sublen个字符追加到一个对象上去
  3. 第三个是追加一个字符串
  4. 第四个是追加字符串的前n个
  5. 第五个是追加n个c字符

5. operator[]运算符重载

在这里插入图片描述这个运算符重载是比较强大的,在c语言中,它就是一个解引用。在string中也可以实现类似的功能,比如说当我们想要遍历一个string对象的时候,我们就可以利用这个下标和operator[]运算符重载实现,注意这个size成员函数用于计算对象有效字符的个数的。

int main()
{
	string s1("hello world");
	cout << s1 << endl;
	
	for (size_t i = 0; i < s1.size(); i++)
	{
		cout << s1[i];
	}
	cout << endl;
	return 0;
}

在这里插入图片描述
我们知道size成员函数是计算有效字符的个数的,而\0字符并不是有效字符,他是一个特殊的标识字符。所以第二次遍历的时候并不会遍历到\0
在这里插入图片描述而如果我们遍历的时候在size函数后+1,是可以访问到\0的,但是编译器不会显示的打印出来,所以打印结果还是原来的样子
在这里插入图片描述
我们还注意到,operator[]返回的是引用,所以是可以直接进行修改的。由此我们有了下面的代码
在这里插入图片描述
这样一来string就可以像数组一样玩了。
但是需要注意的是,它与数组的解引用是具有天差地别的,底层是完全不一样的
在这里插入图片描述

三、String迭代器

1.迭代器介绍

迭代器是一个像指针一样的东西
我们知道,string类的底层是这样的一个顺序表。这样就可来进行增删查改
在这里插入图片描述而迭代器就是增加了一种访问的方式,这里要注意,迭代器只是像指针的一样的东西,而不一定是指针,我们可以暂且理解为指针。

我们来分析下面的代码,注意迭代器的使用必须是string::iterator。
这段代码中,begin和end可以指向的的是起始的位置,和末位置的后一个位置,由于末位置是最后一个字符,所以end指向的是'\0'字符

int main()
{
	string s1("hello world");
	cout << s1 << endl;
	string::iterator it = s1.begin();
	while (it != s1.end())
	{
		cout << *it << ' ';
		++it;
	}
	cout << endl;
	return 0;
}

在这里插入图片描述
注意的是iterator是一个像指针的类型,有可能是指针,有可能不是指针

如下代码所示,它可以实现类似指针的功能,修改所指向的空间,也就是说迭代器可以读写数据

int main()
{
	string s1("hello world");
	cout << s1 << endl;
	string::iterator it = s1.begin();
	while (it != s1.end())
	{
		(*it)--;
		it++;
	}

	it = s1.begin();
	while (it != s1.end())
	{
		cout << *it << ' ';
		++it;
	}
	cout << endl;
	return 0;
}

在这里插入图片描述

2.string中迭代器的作用

我们知道,使用operator[]运算符也可以实现读写,使用迭代器也可以实现读写。在string中我们起始更喜欢使用运算符重载来读写,因为比较简洁。那么使用迭代器有什么意义呢?
我们知道,我们有时候在读的时候,也喜欢使用范围for来解决。

	for(auto& c : s1)
	{
		c++;
	}
	for (auto c : s1)
	{
		cout << c << ' ';
	}
	cout << endl;

在这里插入图片描述
范围for实际上底层就是使用迭代器来实现的,底层直接替换为迭代器

所以说实际上并没有什么范围for,它只是一层伪装罢了,没有迭代器就不支持范围for,一个类只要支持迭代器就支持范围for,我们已经知道数组和string都是支持范围for的,而栈是没有迭代器的,所以自然不支持范围for,数组支持迭代器是因为指针就是天然的迭代器。

迭代器的一大好处就是任何容器都支持迭代器,并且用法是类似的

我们可以看下面的程序,虽然我们可能暂时不会写出来,但是还是能读懂的

#include<vector>
#include<list>
int main()
{
	vector<int> v;
	v.push_back(10);
	v.push_back(20);
	v.push_back(30);
	v.push_back(40);
	vector<int>::iterator vit = v.begin();
	while (vit != v.end())
	{
		cout << *vit << ' ';
		vit++;
	}
	cout << endl;
	
	list<int> lt;
	lt.push_back(50);
	lt.push_back(60);
	lt.push_back(70);
	lt.push_back(80);
	list<int>::iterator lilt = lt.begin();
	while (lilt != lt.end())
	{
		cout << *lilt << ' ';
		lilt++;
	}
	cout << endl;

	return 0;
}

在这里插入图片描述
对于链表和树形结构就无法使用operator[]了。但是迭代器仍然可以使用。

总结:iterator提供了一种统一的方式访问和修改容器的数据

3.迭代器跟容器结合

我们容器中的数据都是私有的。我们无法访问的,但是如果想要实现算法的话,就必须访问数据,为了访问数据就需要使用迭代器了。因为iterator提供了统一的访问方式和修改容器的数据。
我们知道数组的逆置和链表的逆置是不一样的,但是库里面提供了统一的算法接口。
算法的库名称为<algorithm>
我们可以来看一下库里面的reverse函数,可以看到他是需要传入两个迭代器的,这个迭代器的区间为左闭右开,刚好就是我们迭代器的begin和end
在这里插入图片描述我们来应用一下

#include<vector>
#include<list>
#include<algorithm>
int main()
{
	vector<int> v;
	v.push_back(10);
	v.push_back(20);
	v.push_back(30);
	v.push_back(40);
	vector<int>::iterator vit = v.begin();
	while (vit != v.end())
	{
		cout << *vit << ' ';
		vit++;
	}
	cout << endl;

	list<int> lt;
	lt.push_back(50);
	lt.push_back(60);
	lt.push_back(70);
	lt.push_back(80);
	list<int>::iterator lilt = lt.begin();
	while (lilt != lt.end())
	{
		cout << *lilt << ' ';
		lilt++;
	}
	cout << endl;
	
	reverse(lt.begin(), lt.end());
	reverse(v.begin(), v.end());


	cout << "reverse_vector:";
	vit = v.begin();
	while (vit != v.end())
	{
		cout << *vit << ' ';
		vit++;
	}
	cout << endl;

	cout << "reverse_list:";
	lilt = lt.begin();
	while (lilt != lt.end())
	{
		cout << *lilt << ' ';
		lilt++;
	}
	cout << endl;

	return 0;
}

运行结果为
在这里插入图片描述需要注意的是,我们的顺序表和链表虽然看上去用的是同一个函数,但其实并不是同一个函数,它调用的是函数模板。用函数模板自动生成一个函数。

我们在上面提到过,范围for起始就是迭代器。所以我们可以使用范围for来进行遍历一下数据
在这里插入图片描述
如果我们需要排序的话,可以直接用sort,而不需要使用qsort了,可以看到sort有两个函数重载,我们现在只考虑第一个函数重载,顺序表可以直接使用第一个进行排序,链表不能用第一个。可以看到,只需要传左闭右开的迭代器就可以进行排序了。
在这里插入图片描述在这里插入图片描述

总结:算法可以通过迭代器,去处理容器中的数据

4.反向迭代器

有时候我们还需要反向遍历容器中的数据,这时候就需要我们使用反向迭代器了,反向迭代器的类型是在迭代器前面加上reverse,同样的调用begin和end也变成了rbegin和rend,并且和正向迭代器一样,是左闭右开的

int main()
{
	string s1("hello world");
	string::reverse_iterator rit = s1.rbegin();
	while (rit != s1.rend())
	{
		cout << *rit << ' ';
		rit++;
	}
	cout << endl;
	return 0;
}

在这里插入图片描述

在这里插入图片描述
我们有时候会觉得迭代器的类型太过于繁琐,我们会直接使用auto来进行代替
在这里插入图片描述和正向迭代器类似,反向迭代器也可以直接修改容器的数据
在这里插入图片描述

5.const修饰的迭代器

当我们写出如下代码的时候,我们发现报错了
在这里插入图片描述

这里其实涉及到一个权限放大的问题
我们为了使得传参的代价小一点,不去调用拷贝构造,故而使用了引用,但又因为我们函数内并未涉及到修改s的值,所以我们加上了const修饰,但是这样我们就发现,报错了。这是因为s是const对象,它必须调用const修饰的迭代器
我们查看库里面的,确实又一个const修饰的迭代器
在这里插入图片描述所以我们将迭代器的类型进行修改就可以解决问题了
在这里插入图片描述普通的迭代器可以进行读写,而const修饰的迭代器只能读不能写

在这里为了避免犯错误,我们可以使用auto来代替类型
在这里插入图片描述
同样的,反向迭代器遇到const修饰的对象,也需要使用const修饰的迭代器类型
在这里插入图片描述

在这里插入图片描述

四、String容量相关的成员函数

1.size和length

在这里插入图片描述在这里插入图片描述

size是数据个数,length是长度,这两个在string的结果是一致的
在这里插入图片描述这两个的功能是一样的,但是却有两个函数这其实就与string的历史有一些关系,string是在STL产生之前的。当时使用的是length,因为与c语言中的strlen是一致的。但是后来为了在STL中提供统一的接口,就采用了size了。因为对于树形结构等,用lenth这个单词并不合适。而使用size是比较合适的

2.max_size

在这里插入图片描述这个函数可以返回最大的数据个数。
在这里插入图片描述但是要小心,在不同的编译器上,这个函数可能会产生不一样的结果

这是因为STL是一个规范,它有很多版本,比如PJ版本SGI版本。他们的底层实现大同小异

3.capacity

这个函数顾名思义,是计算当前的容量的,同样的,这个函数也要小心,不同的编译器运行结果有可能不同
在这里插入图片描述
这里是本应该是16,但是最后一个是\0,故少了一位。容量就是15了

我们可以在vs上分析一下扩容的过程,如下代码所示,可以看到大概是呈1.5倍的速度扩容的

int main()
{
	string s("hello world");
	cout << s.max_size() << endl;
	cout << s.capacity() << endl;

	size_t old = s.capacity();
	for (size_t i = 0; i < 100; i++)
	{
		s += 'x';
		if (old != s.capacity())
		{
			cout << "扩容" << s.capacity() << endl;
			old = s.capacity();
		}
	}
	return 0;
}

在这里插入图片描述
而在Linux环境下,是呈2倍的速度进行扩容的

4.clear

clear的作用是清理字符串的内容,使其成为空字符串
在这里插入图片描述在这里插入图片描述
我们可以注意到,size会被改变,但是capacity不会被改变

5.reserve

这里我们首先需要注意的是:不要与reverse这个单词搞混了
reverse: 这个单词的意思是反转
reserve: 这个单词的意思是保留

在这里插入图片描述

reserved的功能是请求一个容量的改变
在这里插入图片描述

那么我们应该如何使用呢?在前面使用capacity的时候,我们可以观测到string一个对象的容量的改变,而如果我们对对象使用这个成员函数,那么就可以直接改变容量,这样的好处就在于不需要扩容了
如下代码所示

int main()
{
	string s;
	s.reserve(100);

	cout << s.max_size() << endl;
	cout << s.capacity() << endl;

	size_t old = s.capacity();
	for (size_t i = 0; i < 100; i++)
	{
		s += 'x';
		if (old != s.capacity())
		{
			cout << "扩容" << s.capacity() << endl;
			old = s.capacity();
		}
	}
	cout << s.size() << endl;
	cout << s.capacity() << endl;

	s.clear();

	cout << s.size() << endl;
	cout << s.capacity() << endl;



	return 0;
}

我们可以观测到实际上是要比100大一些的
在这里插入图片描述

但是在Linux下,开的容量正好是100,在此不做演示了

所以这个函数具体开多大的空间还取决于编译器。但总的来说,来的容量必须比100要大。不能比100少

那么当比原来string对象的容量要小的时候,会发生什么情况呢,根据文档的描述,可能会缩容。
但是在实际中,缩小容量与否取决于编译器

下面是vs2022的结果,并没有缩小容量。
在这里插入图片描述但是需要注意的是,当我们将这个reserve放到clear后面的时候,缩小了。但是并没有完全缩
在这里插入图片描述
而在Linux环境下, 确确实实缩小了容量,即要缩到多少,就缩小到多少。但是有个前提是跟clear有关系。

6.resize

对于这个函数他的作用是填值加开空间,他的作用与resever类似,但是比resever要多了一个填值的过程
在这里插入图片描述下面是他们两个的对比
在这里插入图片描述
还需要注意的是,他的填值是由自己来决定的,如果我们不带上这个字符,默认填0

还需要注意的是,如果n比我们当前的数据要多,这自然是我们最期望的状态。但倘若n比我们的数据个数要小,这样的话,我们就会删一些数据
在这里插入图片描述在这里插入图片描述也就是说,他是一定会使得size减少,但是容量不会缩容的

7.shrink_to_fit

这个函数的作用是,收缩至合适,即将capacity调整至size
在这里插入图片描述但是这个函数也是不具有约束力的,即不一定会收缩至和size一样,可能会比size大一些

五、一些经典的力扣题

1.仅仅反转字母

题目链接:仅仅反转字母

这道题思路很简单,我们采用双指针,利用快速排序的思路即可。isalpha()函数是判断一个字符是否为英文字母,若是返回真,否则返回假

class Solution {
public:
    string reverseOnlyLetters(string s) {
        int end=s.size()-1;
        int begin=0;
        while(begin<end)
        {
            while((begin<end)&&(!isalpha(s[begin])))
            {
                begin++;
            }
            while((begin<end)&&(!isalpha(s[end])))
            {
                end--;
            }
            swap(s[begin],s[end]);
            begin++;
            end--;
        }
        return s;
    }
};

2.字符串相加

题目链接:https://leetcode.cn/problems/add-strings/

对于这道题,我们的思路是逐位相加。从末尾开始一步一步相加。一开始让进位端为0,然后将相加端都给提取出来,让这三个相加,得到一个数,然后考虑进位,然后将字符给一位一位加上去,最后逆置字符串即可。

class Solution {
public:
    string addStrings(string num1, string num2) {
        int end1=num1.size()-1;
        int end2=num2.size()-1;
        string s;
        int carry=0;
        while(end1>=0||end2>=0)
        {
            int val1= (end1>=0)?num1[end1]-'0':0;
            int val2= (end2>=0)?num2[end2]-'0':0;
            end1--;
            end2--;
            int ret=val1+val2+carry;
            carry=ret/10;
            ret=ret%10;
            s+=ret+'0';
        }
        if(carry>0)
        {
            s+='1';
        }
        reverse(s.begin(),s.end());
        return s;
    }
};

当然我们也可以采用头插的方式,由于库中并没有给出头插的方式,因为头插的效率太低,string的底层是一个顺序表,我们知道对于顺序表更喜欢尾插。虽然没有头插,但是提供了insert的接口
在这里插入图片描述我们使用第六个成员函数即可。将一个字符插在某个迭代器之前

class Solution {
public:
    string addStrings(string num1, string num2) {
        int end1=num1.size()-1;
        int end2=num2.size()-1;
        string s;
        int carry=0;
        while(end1>=0||end2>=0)
        {
            int val1= (end1>=0)?num1[end1]-'0':0;
            int val2= (end2>=0)?num2[end2]-'0':0;
            end1--;
            end2--;
            int ret=val1+val2+carry;
            carry=ret/10;
            ret=ret%10;
            //s+=ret+'0';
            s.insert(s.begin(),ret+'0');
        }
        if(carry>0)
        {
            //s+='1';
            s.insert(s.begin(),'1');
        }
        //reverse(s.begin(),s.end());
        return s;
    }
};

虽然两种方式都可以,但是综合来看,第一种的效率更佳,总时间复杂度为O(N),第二种的总时间复杂度为O(N2)。

六、String其他接口

1.operator[]操作符

这个操作符,在文章我前面已经介绍过,这里在此简单的介绍一下
在这里插入图片描述这个函数可以去访问string某个下标的元素,他有两个函数重载,一个是加了const修饰的,一个是未修饰的,函数在使用时候会自动匹配最合适的一个函数来进行调用

2.at

在这里插入图片描述
at其实与operator[]几乎是一样的。
在这里插入图片描述at和operator[]唯一的不同之处就在于对于越界的检查是不一样的

当发生越界的时候,at是抛异常的方式,operator[]则是断言的方式
即at比较温柔一点,operator[]比较暴力

3.assign

assign这个接口的功能是赋值,他有以下的函数重载
在这里插入图片描述
如下是他与append的对比
在这里插入图片描述

4.insert

insert顾名思义就是在某个位置插入,他有如下的函数重载:
在这里插入图片描述

这些函数的功能也是比较明确简单的

如下代码是部分函数的演示

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

	s1.assign("xxxxxx");
	cout << s1 << endl;

	s1.insert(0, "hello");
	cout << s1 << endl;
	
	s1.insert(5, "world");
	cout << s1 << endl;

	s1.insert(0, 10, 'x');
	cout << s1 << endl;


	s1.insert(s1.begin()+10, 10, 'y');
	cout << s1 << endl;
	return 0;
}

在这里插入图片描述

insert虽然看上去不错,但是效率比较低下,不宜多用。

5.erase

erase用于从pos位置删除n个字符
在这里插入图片描述
如下所示是这个函数的演示
在这里插入图片描述

6.replace

顾名思义,这个函数的意思是替代
在这里插入图片描述

如下所示是replace函数的一些使用
在这里插入图片描述
然而这个函数看似方便,实际上效率极低。一般不会轻易使用这个函数
比如说当我们想要将某个字符串的空格替换为20%的时候,我们可以使用这个函数,但是效率太低,不满意。我们可以使用其他的函数来实现这个功能

int main()
{
	string s1("hello world hello world");
	string s2;
	for (auto ch : s1)
	{
		if (ch != ' ')
		{
			s2 += ch;
		}
		else
		{
			s2 += "20%";
		}
	}
	s1 = s2;
	cout << s1 << endl;
}

在这里插入图片描述

7.c_str

这个函数的功能是返回他底层的字符串
在这里插入图片描述在这里插入图片描述
这个函数一般是用于跟c的一些接口函数进行配合

int main()
{
	string filename = "test.cpp";
	FILE* fout = fopen(filename.c_str(), "r");
	return 0;
}

我们使用string类型而不是直接使用字符串的好处就在于,string的库非常丰富。我们可以直接去进行调用

8.find

顾名思义,他的功能就是查找,有如下几个函数重载
在这里插入图片描述
如果找到了对应的内容,就返回这个位置的下标

在这里插入图片描述

9.substr

find可以很方便的找到要找到内容的起始下标。而substr的功能则是取出字符类中字符串中的某一部分,并且返回这一部分
在这里插入图片描述这样的话,我们就可以使用find和substr进行搭配,来进行网站的分割

我们知道网站分为三部分:协议,域名,资源
我们对一个网站进行分割的代码如下所示

int main()
{
	string ur1 = "https://home.firefoxchina.cn/?fromwww";
	size_t pos1 = ur1.find("://");
	//协议
	string protocol;
	if (pos1 != string::npos)
	{
		protocol = ur1.substr(0, pos1);
	}
	//域名和资源
	string domain;
	string uri;
	size_t pos2 = ur1.find('/', pos1 + 3);
	if (pos2 != string::npos)
	{
		domain = ur1.substr(pos1 + 3, pos2 - (pos1 + 3));
		uri = ur1.substr(pos2 + 1);
	}
	cout << protocol << endl;
	cout << domain << endl;
	cout << uri << endl;

	return 0;
}

在这里插入图片描述

10.rfind

rfind和find类似,只不过他是从后往前找,这样的好处在于找一个文件的后缀,因为一个文件可能会有多个后缀,所以我们显然不可能从前往后找,我们必须从后往前找,故
在这里插入图片描述

11.find_first_of

从名字上来说,它的功能似乎与find类型?
其实不然,虽然它的参数也和find一样,但它的功能是从所给的字符串中,找到第一个和这里面中的任何一个字符相同的字符位置
在这里插入图片描述
如下代码所示,功能就是找出这句话中aeiou的位置,并将其替换为*字符

在这里插入图片描述

12.find_last_of

这个函数的功能和上面的是十分类似的,唯一不同的就是它是从后往前找的
在这里插入图片描述

13.relational operators

这里面其实就是一些运算符重载,其实里面的函数实现是存在一些冗余的,只需要实现对象和对象之间的比较即可,其他的都有隐式类型转换
在这里插入图片描述

14.operator+

这个运算符重载也很好理解,就是两个字符串进行相加,但需要注意的是,相加后是不改变原来的对象的

在这里插入图片描述

15.getline

这个函数的功能需要与cin对比来看,cin相当于scanf,getline相当于gets。cin当遇到空格时就不读了。而getline可以自己设置结束读取标志,或者默认换行时候才结束,这就在当我们读取一个句子的时候,由于有空格,我们就必须使用getline函数了

我们可以看下面这个例子
字符串的最后一个单词长度
下面才是正确的代码

#include <iostream>
using namespace std;

int main() 
{
    string s;
    getline(cin,s);
    int len=s.size();
    int pos=s.rfind(' ');
    if(pos!=string::npos)
    {
        cout<< len-(pos+1);
    }
    else 
    {
        cout<< len;
    }
}

16.string转为其他类型

在c语言中字符串转为其他类型时候有两个函数可以去调用:atoi,itoa
但是这两个函数其实并不是很好用

在c++中,我们有这样一系列的函数可以进行转换
在这里插入图片描述

17.其他类型转化为string类型

有以下函数重载可以实现这个功能
在这里插入图片描述

七、string与模板

我们也许会发现,string貌似跟类模板关系不是很大。
但其实string也是一个模板出来的

我们可以注意到,string其实typedef出来的
在这里插入图片描述类似的,其实还有很多的string
在这里插入图片描述

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青色_忘川

你的鼓励是我创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值