【C++】string类的基本使用

一、string类的由来

在C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列
的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户
自己管理,稍不留神可能还会越界访问。

面向过程编程OPP:Procedure Oriented Programming,是一种以事物为中心的编程思想。主要关注“怎么做”,即完成任务的具体细节。

面向对象编程OOP:Object Oriented Programming,是一种以对象为基础的编程思想。主要关注“谁来做”,即完成任务的对象。

于是在C++设计过程中,就添加了string类,用来专门管理字符串。

 string这个类是被typedef出来的,basic_string是一个类模板,string内部原理可以理解为是一个动态开辟的顺序表,每个元素是一个char类型的字符。我们知道一说到字符串一定牵扯到编码问题,比如我们汉字的编码(unicode)和英文字母(ascii)的编码方式可能就会不同,常见的编码方式有ASCII,UTF-8,UTF-16,UTF-32等等,string是用模板实例化出来的一个类,它的编码方式就是常见的UTF-8。UTF-8编码的一个主要优点是它向后兼容ASCII,即任何ASCII字符在UTF-8中都使用相同的单字节表示。所以,它的成员变量都是char类型。有人会问为什么不直接搞个ASCII编码的字符串类出来,那就忽略了一个问题,汉字字符串怎么办?

u16string也是通过basic_string模板实例化出来的一个类,它的编码方式是UTF-16。

u32string同样是通过basic_string模板实例化出来的一个类,它的编码方式是UTF-32。

这里我们主要讲string这个类。

 二、string类的基本使用

在使用string类时,必须包含#include<string>这个头文件以及using namespace std;如果不加using namespace std;创建对象时必须指明命名空间(std::string)。本篇主要讲如何使用,深层的东西不过多涉及。

1、构造函数

C++98版本下有7个构造函数,我们这里只说C++98,不谈论C++11。

接下来我们来看看它们是怎么使用的:

int main()
{string s1;  //(1)默认构造函数,空串string s2("hello world"); //(4)传参构造string s3(s2);  //(2)拷贝构造cout << s1 << endl;  //重载了流插入,能够打印输出string类型的对象cout << s2 << endl;cout << s3 << endl;//cin >> s1;  //也重载了流提取,能够向string类型的对象中输入值//cout << s1 << endl;  string s4(s2, 6, 5);//(3)s2中,下标为6的字符向后拷贝5个字符初始化给s2//假设s2下标为6的字符后的字符不够5个,则拷贝到结尾即可cout << s4 << endl;string s5(s2, 6);//(3)//我们可以看到库中第三个参数有个缺省值npos(size_t类型)//它是类中静态成员变量,值为-1,-1在内存中存储就是32个1,//因为npos是size_t类型也就是无符号整形,即npos就是整数的最大值//它表示的意思就是从某个下标位置开始拷贝到结尾cout << s5 << endl;string s6("hello world",5);//(5)拷贝第一个参数字符串的前5个字符初始化给s6cout << s6 << endl;string s7(3,'x'); //(6)用3个'x'字符初始化给s7cout << s7 << endl;return 0;
}

运行结果:

2、析构函数

析构函数我们不需要使用,因为编译器会自动帮我们调用来释放空间。构造函数需要我们写是因为初始化的形式是多样的。

3、赋值重载

int main()
{string s1("hello world");string s2("xxx");cout << s1 << endl;cout << s2 << endl;s1 = s2; //(1)对象参数类型重载cout << s1 << endl;cout << s2 << endl;s1 = "hah"; //(2)字符串参数类型重载cout << s1 << endl;cout << s2 << endl;s2 = 'q'; //(3)字符参数类型重载cout << s1 << endl;cout << s2 << endl;return 0;
}

4、重载[]

string可以像其他内置类型一样直接用下标引用操作符[]来访问内部元素。像下面这样:

int main()
{string s1("hello world");cout << s1[0] << endl; //hcout << s1[1] << endl; //ereturn 0;
}

我们知道自定义类型不能直接通过下标引用操作符来访问内部元素,而string的底层其实是在类内对下标引用操作符[]进行了重载。我们可以简单想象一下它的实现:

class string
{
public:char& operator[](int i){assert(i <= _size);return _str[i];}
private:char* _str; //指向空间的起始位置int _size; //记录元素个数int _capacity; //由于要扩容,所以这里需要记录容量大小
};

 能用引用返回吗?为什么要引用返回呢?

开辟空间是在堆上开辟的,调用[]结束后,空间还在,所以能用引用返回。至于为什么要用引用返回,第一,减少一次拷贝构造;第二,也是最重要的一点,可以修改变量的值。这也和下标引用操作符的功能进行了重合。

int main()
{string s1("hello world");cout << s1[0] << endl; //hcout << s1[1] << endl; //es1[0] = 'x'; //支持修改,也印证了我们的想象s1[1] = 'x';cout << s1[0] << endl; //xcout << s1[1] << endl; //xreturn 0;
}

内置类型越界访问数组程序不会崩溃,也不会报错。

int main()
{int a[3] = { 1,2,3 };cout << a[5] << endl; //越界访问return 0;
}

 运行结果:

说明了越界访问,编译器也不管,但我们在类中重载下标引用操作符时,如果越界访问,直接报错,这样就会更好。所以我们加了一句"assert(i < _size);",可以避免发生越界情况。

我们可以来验证一下我们的猜想是否正确:

由此可见,string底层就是有这一机制的。 

在此,介绍几种遍历成员的方式:

int main()
{string s("hello world");//方式1for (size_t i = 0;i < s.size();i++){cout << s[i] << " ";}cout << endl;//方式2//iterator是STL六大组件之一的迭代器//使用迭代器必须指明在哪个容器的类域,每个容器都有自己的迭代器,它们的名字相同,用法相同但内部结构可能"天差地别",用法相同说明了在其他容器中也可以用这种方式来遍历成员//用迭代器定义出来的对象,功能上像指针,可能是指针也可能不是指针,这里暂且理解为指针,也可以理解为像指针的东西//begin()是返回这段空间开始位置的迭代器,end()是返回最后一个有效元素的下一个位置的迭代器string::iterator it = s.begin();while (it != s.end()){cout << *it << " "; //这里可能就有人说了,不是指针怎么解引用,不是指针可能会对*进行重载,这里的*it如果改变就会修改s中字符的值it++; //这里同样也是,若不是指针,就会对++进行重载}cout << endl;//方式3(C++11)//范围for:从s这个容器中自动取值给e,直到取完为止//auto自动推导类型,这里的auto也可以写成char,但一般都写auto//自动赋值,自动迭代,自动判断结束//它的底层其实是迭代器,*it的值赋给e,支持迭代器就支持范围forfor (auto e : s) //写起来更简单{cout << e << " "; //这里的e只是s中每个字符的拷贝,修改e的值不影响s中字符的值,若想修改在auto后面加上引用&}cout << endl;return 0;
}

这3种方式在性能上没有区别,都是遍历,只是写法上不同。

(1)、auto(C++11语法)

在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,会自动释放,后来局部变量都能自动释放,所以auto在这里就不重要了。C++11中,标准委员会变废为宝赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得

如果一个对象的类型太长,我们就可以用auto来简化代码。比如:

	map<string, string> dict;map<string, string>::iterator mit = dict.begin();auto mit = dict.begin();

虽然auto可以简化代码,但在一定程度上"牺牲了"可读性。

int main()
{int a = 10;auto b = a;cout << typeid(b).name() << endl; //打印int,typeid可以查看对象类型return 0;
}

 auto不能去定义数组。auto不能做参数但可以做返回值,auto做返回值建议谨慎使用。auto可以同时定义两个对象,但对象的类型必须一致,否则会报错。

auto后跟*,代表是指针,必须给地址,否则会报错。

(2)、范围for(C++11语法)

范围for主要用于容器。

for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束。

范围for可以作用到数组和容器对象上进行遍历,范围for的底层很简单,容器遍历实际就是替换为迭代器。

范围for用来遍历数组也是很方便的:

int main()
{int arr[] = { 1,2,3,4,5 };for (auto e : arr){cout << e << " ";}cout << endl;return 0;
}
 (3)、迭代器
int main()
{string s1("hello world");//1、正向迭代器string::iterator it = s1.begin();while (it != s1.end()){cout << *it << " "; it++; }cout << endl;//2、反向迭代器string::reverse_iterator rit = s1.rbegin();while (rit != s1.rend()){cout << *rit << " ";rit++; //逆向走,++被重载了}cout << endl;const string s2("day day up");//3、const迭代器,只能读,不能写(修改指针指向的内容)//cbegin和cend专门用于const迭代器,它们的功能和begin/end一样//这里用begin/end来替代cbegin/cend也可以string::const_iterator cit = s2.cbegin();while (cit != s2.cend()){cout << *cit << " ";cit++; }cout << endl;//4、const反向迭代器,只能读,不能写(修改指针指向的内容)//crbegin和crend专门用于const反向迭代器,它们的功能和rbegin/rend一样//这里用rbegin/rend来替代crbegin/crend也可以//string::const_reverse_iterator crit = s2.crbegin();auto crit = s2.crbegin();while (crit != s2.crend()){cout << *crit << " ";crit++;}cout << endl;return 0;
}

 begin指向第一个有效元素,end指向最后一个有效元素的后一个位置。

 rbegin指向最后一个有效元素,rend指向第一个有效元素的前一个位置。

5、成员函数

string类中成员函数有许多,在这里只写一部分常用到的,对于所写的每个成员函数我会写一些用法代码帮助大家理解,但有些成员函数有多个重载,我不会把每个重载的用法都写一遍,只挑选一些来写,希望大家理解。

(1)、size() / length()

这两个成员函数的功能都是返回字符串的长度,但不包括'\0';

(2)、max_size()

它的功能是返回最大字符串的长度(这里是整形的最大值)。

(3)、capacity()

它的功能是返回申请容量大小。这个大小不包括'\0',假设capacity的初始值是15字节,字符串中有15个字符,它是不会扩容的,当字符串中有16个字符它才扩容。也就是说实际的空间是比容量多一个字节的,这一个字节用来存放'\0'。

int main()
{string s1("a");cout << s1.capacity() << endl; //容量为15字节s1 = "aaaaaaaaaaaaaaa"; //15个字符cout << s1.capacity() << endl; //容量为15字节s1 = "aaaaaaaaaaaaaaaa"; //16个字符cout << s1.capacity() << endl; //容量为31字节
}

运行结果: 

我们可以用一个例子来测试容量的变化:

void TestPushBack()
{string s;size_t sz = s.capacity();cout << "capacity of start:" << sz << endl;//扩容前容量cout << "making s grow:" << endl;for (int i = 0;i < 100;++i) //循环控制插入100个字符{s.push_back('c'); //尾插一个字符'c'if (sz != s.capacity()){sz = s.capacity(); //扩容后新的容量给szcout << "capacity changed:" << sz << endl;}}
}
int main()
{TestPushBack();return 0;
}

运行结果:

 我们知道容量是只包含有效数据不包含'\0',但实际空间要比capacity的值多1用来存放'\0',我们在运行结果的基础上每个值都加1才是实际的空间。

加1后我们发现第一次空间是扩二倍,接下来差不多都是1.5倍左右扩容。

这里的原因是什么呢?

VS2019在这里自己做了单独的处理,当size小于16时,它将元素存放在一个buff数组中而不是直接存放在堆上。

//VS下多了一个类似_buff的一个数组
class string
{
private:char _buff[16];char* _str;int _size;int _capacity;
};

如果_size小于16,就会存放在_buff中,在没有数据时先给你开16字节的空间,capacity大小是15(加上'\0'就是16);大于16就全部存放在_str指向的堆中,同时清空_buff,但_buff这个空间还在,_size大于16首次扩容就会扩到32,这是单独处理的。后续扩容就是1.5倍左右。

我们可以看一下一个string类对象的大小是多少:

int main()
{string s;cout << s.capacity() << endl;cout << sizeof(s) << endl;    return 0;
}

 运行结果:

这里的15就是没有数据时,capacity的大小,验证了我们上面说的在没有数据时先给你开16字节的空间,capacity大小是15(加上'\0'就是16)

而28就是_buff占16字节,char*占4字节,_size占4字节,_capacity占4字节,一共占28字节。

同样一段代码在Linux环境下的结果是不同的:

我们可以看到,在Linux下它是严格的二倍扩容,它没有_buff这一说,因为开始时capacity的值为0。 

为什么在两种不同的环境下会有差异呢?

C++标准规定,string类必须实现什么功能,但怎么实现的是靠编译器来决定的。比如扩容,C++标准规定string类要实现自动扩容,但怎么扩容,扩多大是每个编译器自己实现的,这里VS和Linux下的扩容机制就不大相同。

(4)、reserve()

它的功能是改变容量的,也就是改变capacity的大小,它可以避免频繁扩容。

假设参数是n(就是改变后的容量大小),分3种情况:

1、n < size

首先会不会缩容,这个问题是根据编译器的,有些编译器会缩容,有些编译器不会缩容。如果缩容,则最多缩到size,不能把我的size也给缩没了。

在VS2019下是不缩容的,即容量保持不变,当然size也不会改变,其中的元素也不会改变。

在Linux下可能缩容。

2、size < n < capacity

有些编译器会缩容,有些编译器不会缩容。

在VS2019下是不缩容的,即容量保持不变,当然size也不会改变,其中的元素也不会改变。

在Linux下可能缩容。

3、n > capacity

会扩容,至少扩到n,也可能更多,这是不确定的。在vs下通常会扩的更多一些,而在Linux下通常就扩到n。

int main()
{string s1("aaaaa");cout << s1.size() << endl; //元素个数为5个cout << s1.capacity() << endl; //容量为15字节//1、n < size s1.reserve(3);cout << s1.size() << endl; //元素个数为5个cout << s1.capacity() << endl; //容量为15字节//2、size < n < capacitys1.reserve(10);cout << s1.size() << endl; //元素个数为5个cout << s1.capacity() << endl; //容量为15字节//3、n > capacitys1.reserve(50);cout << s1.size() << endl; //元素个数为5个cout << s1.capacity() << endl; //会扩容,容量为63字节}
(5)、resize()

功能就是将元素个数设置为n。分3种情况:

int main()
{string s1("hah");cout << s1.size() << endl;  //3cout << s1.capacity() << endl; //15//1.n < sizes1.resize(1);cout << s1.size() << endl;  //1cout << s1.capacity() << endl; //15//2.size < n <capacity s1.resize(10);cout << s1.size() << endl;  //10cout << s1.capacity() << endl; //15//3. n > capacitys1.resize(30);cout << s1.size() << endl;  //30cout << s1.capacity() << endl; //31return 0;
}

在VS2019下运行的结果:

总结一句话,通过resize,设置n为多少,size就跟着变为多少,如果n > capacity,则capacity跟size一起变化,否则只有size变为n,capacity通常不变(取决于编译器)。

如果n小于size,则size个数就会变成n,size中多余的数据被删除。如果n大于size,则size扩大到n,新增加的数据初始化为'\0'。resize也可以传第二个参数,指定一个字符,新增加的数据初始化为你传过去的字符。

(6)、clear()

功能是清除数据,但通常不清除容量。

int main()
{string s("hah");cout << s.size() << endl;cout << s.capacity() << endl;s.clear();cout << s.size() << endl;cout << s.capacity() << endl;return 0;
}

运行结果:

(7)、empty()

功能是判断字符个数是否为空。

int main()
{string s1("hah");if (s1.empty())cout << "s1 -> null" << endl;elsecout << "s1 -> not null" << endl;string s2;if (s2.empty())cout << "s2 -> null" << endl;elsecout << "s2 -> not null" << endl;return 0;
}

运行结果:

(8)、shrink_to_fit()

功能是缩容,将capacity减小到适应它的size。这不是强制的。

int main()
{string s1("hello");cout << s1.size() << endl;cout << s1.capacity() << endl;s1.shrink_to_fit();cout << s1.size() << endl;cout << s1.capacity() << endl;return 0;
}

运行结果:

这里并没有缩容。

(9)、at()

at的功能和重载[]几乎一样,只不过重载[]如果越界会断言报错,at越界会抛出out_of_range的异常。

int main()
{string s("hah");cout << s.at(0) << endl;cout << s.at(1) << endl;cout << s.at(2) << endl;return 0;
}

运行结果:

(10)、front() 

功能是返回第一个字符。

int main()
{string s1("hello");cout << s1.front() << endl; //hreturn 0;
}
 (11)、back()

功能是返回最后一个有效字符。

int main()
{string s1("hello");cout << s1.back() << endl; //oreturn 0;
}
(12)、push_back()

功能是在原有字符串的基础上追加一个字符。

int main()
{string s("hello");cout << s << endl;s.push_back(' ');s.push_back('w');s.push_back('o');cout << s << endl;return 0;
}

运行结果:

 

(13)、pop_back()

 功能是删除字符串的末尾的一个有效字符。

int main()
{string s("hello");cout << s << endl;s.pop_back();cout << s << endl;return 0;
}

运行结果:

 

(14)、append()

它的功能是在原有字符串的基础上追加字符串,但不能追加字符。

这里以第三个重载函数为例: 

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

运行结果:

(15)、重载+=

它的功能也是在原有字符串的基础上追加字符串,也能追加字符。

这里以第二个重载函数为例:   

int main()
{string s("hello");cout << s << endl;s += " world";cout << s << endl;return 0;
}

运行结果: 

(16)、assign()

它的功能是给一个字符串对象赋值,若之前字符串有内容则就覆盖掉原来的内容,同时size也会改变。

这里以第三个重载函数为例:  

int main()
{string s1 = "hah";cout << s1.size() << endl; //size为3cout << s1.capacity() << endl; //capacity为15s1.assign("x");cout << s1.size() << endl; //这里让size小于原来的size,size=1cout << s1.capacity() << endl; //capacity保持不变,capacity=15string s2 = "xix";cout << s2.size() << endl; //size为3cout << s2.capacity() << endl; //capacity为15s2.assign("xxxxxxxxxxxxxxxxxxxxxxxxxx");cout << s2.size() << endl; //这里让size大于原来的capacity,size=26cout << s2.capacity() << endl;//这里的capacity就会扩容到31string s3 = "pip";cout << s3.size() << endl; //size为3cout << s3.capacity() << endl; //capacity为15s3.assign("xxxxx");cout << s3.size() << endl; //这里让size大于原来的size,小于原来的capacity,size=5cout << s3.capacity() << endl;//capacity保持不变,capacity=15return 0;
}

运行结果:

(17)、insert()

它的功能是在指定位置前(这个位置必须有效,否则运行时会崩溃)插入字符或字符串。

 这里以第三个重载函数为例:

int main()
{string s("world");cout << s << endl;s.insert(0, "hello "); //在字符串中下标为0的位置插入"hello "cout << s << endl;return 0;
}

运行结果:

(18)、erase()

它的功能是删除指定位置的字符串或字符。

 这里以第一个重载函数为例: 

int main()
{string s("hello world");cout << s << endl;s.erase(6, 1);  //在下标为6的位置删除1个字符,如果不写第二个参数,默认值是npos,它是int类型最大值,可以理解为从第一个参数位置开始后面全删cout << s << endl;return 0;
}

运行结果: 

(19)、replace()

它的作用是将字符串中某一段替换成另外一段。

  这里以第三个重载函数为例: 

int main()
{string s("hello world");cout << s << endl;s.replace(5, 1, "%%"); //从下标为5的位置开始往后1个字符替换成"%%"cout << s << endl;return 0;
}

 运行结果:

 

用2个字符替换1个字符也是可以的。多替换少也是可以的。

replace()尽量不要频繁使用,因为它底层牵扯到扩容问题,有时需要挪动大量数据。

(20)、find()

它的功能是从某个位置开始查找某个字符或字符串,若找到则返回第一个被找到的位置的起始位置下标,否则返回npos。npos是string类中的静态成员变量。

 这里以第四个重载函数为例: 

int main()
{string s("hello wor ld");size_t pos1 = s.find(' '); //从下标为0的位置开始找' ',若有多个' ',则返回第一个位置的下标cout << pos1 << endl;size_t pos2 = s.find(' ', pos1 + 1); //从下标为pos1 + 1的位置开始找' ',若有多个' ',则返回第一个位置的下标cout << pos2 << endl;return 0;
}

运行结果:

我们可以结合replace()来实现一个小功能:将一个字符串中所有空格换成'%'

int main()
{string s("h el lo wo r ld");cout << "replace before:" << s << endl;size_t pos = s.find(' ');while (pos != string::npos){s.replace(pos, 1, "%");pos = s.find(' ', pos + 1);}cout << "replace after: " << s << endl;return 0;
}

运行结果:

 还用另外一种实现方法:

int main()
{string s("h el lo wo r ld");cout <<  s << endl;string tmp;for (auto e : s){if (e == ' ')tmp += '%';elsetmp += e;}cout << tmp << endl;return 0;
}

运行结果: 

 

(21)、swap()

它的功能是交换两个string类型对象的成员变量的值。

int main()
{string s1("hah");string s2("pip");cout << "s1 = " << s1 << endl;cout << "s2 = " << s2 << endl;s1.swap(s2);cout << "s1 = " << s1 << endl;cout << "s2 = " << s2 << endl;return 0;
}

运行结果: 

 

通过调试时的监视窗口,也可以看出它们的交换情况:

交换前:

交换后: 

不难看出只有buff没变,其余都交换了。

(22)、c_str()

它的功能是返回底层字符串的指针。它的出现就是兼容C语言的,比如一些C语言的函数参数是char*类型的,不能直接传string类型,必须传char*,我们就可以调用c_str()。转换为char*后末尾会放一个‘\0’。

int main()
{string file;cin >> file;FILE* fout = fopen(file.c_str(), "r"); //fopen第一个参数是const char*,所以这里必须要转换一下char ch = fgetc(fout);while (ch != EOF){cout << ch;ch = fgetc(fout);}fclose(fout);return 0;
}
(23)、substr()

它的功能是从某个位置开始,取长度为n的字串,构造一个string类型的对象进行返回。

int main()
{string s1("hello world");string s2 = s1.substr(0, 5); //拷贝构造,取下标为0的位置开始向后5个字符给s2cout << s2 << endl;return 0;
}

运行结果: 

(24)、rfind()

 它的功能是从某个位置开始从后往前找。找到第一个符合条件的就返回对应的下标。

  这里以第四个重载函数为例: 

int main()
{//获取文件名的后缀string s("Test.txt");size_t pos = s.rfind('.');//从后向前找string tmp = s.substr(pos);cout << tmp << endl;return 0;
}

运行结果:

 这时候就有人说了,将rfind换成find也行啊,rfind感觉没什么用,那么请看下面一种情况:

int main()
{//目的:打印.zipstring s("Test.txt.zip");size_t pos = s.rfind('.');//从后向前找,如果这里是find,就不行了string tmp = s.substr(pos);cout << tmp << endl;return 0;
}

运行结果: 

(25)、 find_first_of()

它的功能是从指定位置开始查找任意个字符(查找的范围是参数中的所有字符),找到返回对应下标。

int main()
{string str("Please, replace the vowels in this sentence by asterisks.");size_t found = str.find_first_of("aeiou"); //找出串中的'a','e','i','o','u'任意首个出现的字符的下标while (found != string::npos){str[found] = '*';found = str.find_first_of("aeiou", found + 1);}std::cout << str << '\n';return 0;
}

运行结果: 

这段代码的功能是将串中的所有'a','e','i','o','u'替换成'*'。

(26)、find_last_of()

它的功能与find_first_of()一样,只不过是从后往前找。这里就不赘述了。

(27)、find_first_not_of()

它的功能是从指定位置开始查找任意个字符(查找的范围是除了参数之外的所有字符),找到返回对应下标。

int main()
{string str("Please, replace the vowels in this sentence by asterisks.");size_t found = str.find_first_not_of("aeiou"); //找出串中不是'a','e','i','o','u'任意首个出现的字符的下标while (found != string::npos){str[found] = '*';found = str.find_first_not_of("aeiou", found + 1);}std::cout << str << '\n';return 0;
}

 运行结果:

 这段代码的功能是将串中的所有不是'a','e','i','o','u'的字符替换成'*'。

(28)、find_last_not_of()

它的功能与find_first_not_of()一样,只不过是从后往前找。这里就不赘述了。

6、非成员函数

(1)重载+

 这里以第二个重载函数为例:  

int main()
{string s1("hello");string s2 = s1 + " world"; //(2)string s3 = "world " + s1;  //(2)cout << s2 << endl;cout << s3 << endl;return 0;
}

运行结果: 

(2)重载关系运算符

支持两个字符串比较大小,比较规则是根据ASCII码值的大小。这里就不举例了。

(3)重载流插入(<<)和流提取(>>)

重载后就支持输入和输出string类型的字符串了。 

(4)getline()

当cin一个字符串对象时,它是取不到空格的:

int main()
{string s;cin >> s;cout << s << endl;return 0;
}

当我们输入AAA B时,它只会取到AAA:

 因为cin默认在遇到换行或空格时停止在缓冲区中继续读数据。

getline就可以解决这个问题,cin默认在遇到换行时停止在缓冲区中继续读数据。

int main()
{string s;getline(cin,s);cout << s << endl;return 0;
}

 当我们输入AAA B时,它会取到AAA B:

它有两个重载函数: 

 它默认是以换行为终止符,我们也可以自己设置终止符,对应的是第一个构造函数。 

三、总结

本篇到这里就结束了,主要讲了string类的基本使用,希望对大家有帮助,祝大家天天开心!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/52629.shtml

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

相关文章

MBD_入门篇_23_SimulinkSinks

23.1 概述 Sink库里面是Simulink的显示或导出信号数据的模块&#xff0c;可以理解为信号的最后接收的模块&#xff0c;要么用于显示要么用于传递给更上层的系统要么终止。 Sink库里面的模块都只有输入&#xff0c;没有输出。 23.2 回顾常用模块 23.2.1 Display 用于实时仿真…

Computer Exercise

每日一练 单选题 在Word2003中&#xff0c;通过&#xff08;     D    &#xff09;不能设置字符格式。 A.格式菜单     B.格式工具栏     C.格式刷     D.常用工具栏数据库系统而言&#xff0c;主要通过权限和授权来实现安全管理&#xff0c;其中update权限表示&am…

数据库——MySQL概述

一、数据库 存储数据的仓库&#xff0c;数据是有组织的存储&#xff0c;简称database&#xff08;DB&#xff09; 二、数据库管理系统 操控和管理数据库的大型软件&#xff08;DBMS&#xff09; 三、SQL 操作关系型数据库的编程语言&#xff0c;定义了一套操作关系型数据库…

Leetcode面试经典150题-134.加油站

解法都在代码里&#xff0c;不懂就留言或者私信 class Solution {public int canCompleteCircuit(int[] gas, int[] cost) {/**如果只有一个加油站&#xff0c;那它本来就在那个为止&#xff0c;0就是它的编号?但是这只是你的想象&#xff0c;题目有个变态规定&#xff0c;自…

python-古籍翻译

题目描述 小理跑到外星人的图书馆去读书。有一本外星古籍&#xff0c;里面的内容是用八进制写成的&#xff1b;但小理只能处理十六进制的数据。请你帮忙写一个翻译软件&#xff0c;帮小理把八进制串翻译成十六进制串。 输入 仅一行&#xff0c;一个八进制字符串 s&#xff0…

创建Java项目,可实现main方法运行,实现对性能数据的处理

1、Android Studio无法执行Java类的main方法问题及解决方法 Android Studio无法执行Java类的main方法问题及解决方法_delegatedbuild-CSDN博客 D:\workspaces\performanceTools\.idea 文件夹下&#xff0c;gardle.xml ,添加依赖 <option name"delegatedBuild"…

QT:音视频播放器

目录 一.播放器设计 二.需要使用的控件 三.选择视频 四.播放视频 五.暂停视频 六.关闭视频 七.播放状态设置 八.切换视频(上一首) 九.切换视频(下一首) 十.设置视频滑块 十一.更新滑块显示 十二.实现效果 十三.代码设计 1.mainwindow.h 2.mainwindow.cpp 一.播放…

预警提醒并生成日志,便于后期追溯的智慧地产开源了

智慧地产视觉监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;从而大大减少企业级应用约95%的开发成本。 AI是新形势下数…

基于Springboot美食推荐小程序的设计与实现(源码+数据库+文档)

一.项目介绍 pc端&#xff1a; 支持用户、餐厅老板注册 支持管理员、餐厅老板登录 管理员&#xff1a; 管理员模块维护、 餐厅管理模块维护、 用户管理模块维护、 商品管…

NAT技术+代理服务器+内网穿透

NAT技术 IPv4协议中&#xff0c;会存在IP地址数量不充足的问题&#xff0c;所以不同的子网中会存在相同IP地址的主机。那么就可以理解为私有网络的IP地址并不是唯一对应的&#xff0c;而公网中的IP地址都是唯一的&#xff0c;所以NAT&#xff08;Network Address Translation&…

Pyspark下操作dataframe方法(1)

文章目录 Pyspark dataframe创建DataFrame使用Row对象使用元组与scheam使用字典与scheam注意 agg 聚合操作alias 设置别名字段设置别名设置dataframe别名 cache 缓存checkpoint RDD持久化到外部存储coalesce 设置dataframe分区数量collect 拉取数据columns 获取dataframe列 Pys…

[数据集][目标检测]智慧农业草莓叶子病虫害检测数据集VOC+YOLO格式4040张9类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;4040 标注数量(xml文件个数)&#xff1a;4040 标注数量(txt文件个数)&#xff1a;4040 标注…

【小沐学OpenGL】Ubuntu环境下glew的安装和使用

文章目录 1、简介1.1 OpenGL简介1.2 glew简介 2、安装glew2.1 命令安装glew2.2 直接代码安装glew2.3 cmake代码安装glew 3、测试glew3.1 测试glewfreeglut3.2 测试glewglfw 结语 1、简介 1.1 OpenGL简介 Linux 系统中的 OpenGL 是一个跨语言、跨平台的应用程序编程接口&#…

多态的概念

多态 所谓的多态其实就是多种形态&#xff0c;它又被分为编译时多态(静态多态) 和 运行时多态(动态多态)。 静态的多态其实就是之前的模版和函数重载&#xff0c;今天我们主要讲动态的多态。所谓的动态多态其实就是相同的函数&#xff0c;完成不同的功能。 这就实现了明明都是…

《数字图像处理(面向新工科的电工电子信息基础课程系列教材)》P84

更正卷积与相关微课中互相关运算动画中的索引。 1-D correlation rectwave 禹晶、肖创柏、廖庆敏《数字图像处理&#xff08;面向新工科的电工电子信息基础课程系列教材&#xff09;》 禹晶、肖创柏、廖庆敏《数字图像处理》资源二维码

【Linux实践】实验一:Linux系统安装与启动

【Linux实践】实验一&#xff1a;Linux系统安装与启动 实验目的实验内容实验步骤及结果1. 下载VMware2. 下载 Linux 操作系统3. 在VMware中安装Ubuntu系统4. 配置Ubuntu系统5. 关机 实验目的 1.掌握Linux系统的安装过程和简单配置方法。 2.掌握与Linux相关的多操作系统的安装方…

【Leetcode算法面试题】-1. 两数之和

文章目录 算法练习题目思路参考答案算法1算法2算法3 算法练习 面试经常会遇到算法题目&#xff0c;今天开启算法专栏&#xff0c;常用算法解析 题目 ** 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&…

为拖延症量身定制的AI工具,让Kimi做我的《每日信息整理助手》

AI不止对传统行业带来巨大的改变&#xff0c;对日常生活也便利了不少&#xff0c;现在这个时代获取信息的方式太简单了。 我们每天都会接受大量的信息&#xff0c;难免一天下来会忘记很多事情&#xff0c;有时候突然想起了一个点子&#xff0c;有时候突然有一件急事、一件待办事…

17 个被动和主动遥感之间的区别

摘要: 遥感是指通过使用连接到卫星的传感器记录有关地球表面信息的行为。遥感在收集大面积信息、表征地球上的自然特征、观察和监测地球和物体随时间的变化以及 利用这些信息进行处理和分析方面发挥着至关重要的作用。在遥感中,太阳是终极能源,对照明非常有用。卫星具有成像传…

LCS—最长公共子序列

最长公共子序列问题就是求出两个字符串的LCS长度&#xff0c;是一道非常经典的面试题目&#xff0c;因为它的解法是典型的二维动态规划。 比如输入 str1 "babcde", str2 "acbe"&#xff0c;算法应该输出3&#xff0c;因为 str1 和 str2 的最长公共子序列…