C++ primer读书笔记——3.1 命名空间的using声明 & 3.2 标准库类型string(2019.9.1)

3 字符串、向量和数组

引入

笔试题:
在这里插入图片描述
解答:
测试代码:

#include <iostream>
using namespace std;
int main()
{
	char p[]="2018";
	string p1="2018";
	char *p2="2018";
	cout<<p<<endl;
	cout<<p1<<endl;
	cout<<*p2<<*(p2+1)<<*(p2+2)<<*(p2+3);
}

运行结果:
在这里插入图片描述
同时会给出警告信息:c++禁止将字符串常量转变为char*,但是又没有报错,还可以正常运行,这里p可以理解为指向字符串常量(本质是字符串数组)首元素的指针。
在这里插入图片描述
根据运行结果,及理论支持:c++中存储字符串有两种方法,c++string方法和c串方法,
string做法:string str="hello";
c串方法:char str[]="hello";
选A

除了内置类型,c++还定义了一个内容丰富的抽象数据类型库,string和vector是两种重要的标准库类型,string支持可变长字符串,vector支持可变长集合。迭代器也是标准库类型,是string和vector的配套类型,常用于访问string或者vector中的元素。
除了内置类型,标准库定义了一组具有更高级性质的类型,与内置类型与硬件密切相关不同,它们尚未直接实现到计算机硬件中
内置数组与string、vector相比,灵活性不足。

3.1 命名空间的using声明

在了解标准库类型之前,先学习访问标准库中名字的简单方法。
访问标准库中名字的方式

std::cin>>a; //方式一:命名空间::函数名,每次使用前都需要前缀,比较繁琐
using std::cin; //方式二:使用using声明:using 命名空间::函数名,声明了上述语句可直接访问命名空间中的名字
using std::cout;//方式二注意事项:每个名字都需要独立的using声明

注意:头文件不应包含using声明,因为头文件本身的内容会拷贝到引用它的文件中,不经意的包含可能会造成名字冲突。

3.2 标准库类型string

使用string类型必须包括以下using声明和#include指令:

#include <string> //包含string头文件
using std::string; //访问标准库的string名字

标准库类型对于一般应用场合具有足够的效率,因此能使用标准库类型尽量不要使用自定义类型。

3.2.1 定义和初始化string对象

如何初始化类的对象是由类本身决定的,一个类可以定义很多种初始化对象的方式,这些方式之间必须有所区别。
初始化string对象的方式:

string s1; //默认初始化,s1是空串
string s2(s1); //s2是s1的副本
string s2=s1; //s2是s1的副本
string s3("value"); //s3是字面值“value”的副本,除了最后一个空字符
string s3="value"; //s3是字面值“value”的副本,除了最后一个空字符
string s4(n,'c'); //把s4初始化为由连续n个字符c组成的串

直接初始化和拷贝初始化:

直接初始化拷贝初始化
不使用等号使用等号
初始化一个或多个值初始化一个值

总:c++中不同的初始化方式有哪些?直接初始化、拷贝初始化=、默认初始化、列表初始化{}

3.2.2 string对象上的操作

补充:c风格字符串和string类型字符串的区别:c风格字符串(或字符串字面值)以“\0”结尾。string类型不包含‘\0’,c风格字符串实际上是使用 null 字符 ‘\0’ 终止的一维字符数组。
一个类处理要规定初始化其对象的方式外,还要定义对象上能执行的操作。关于操作,类既能定义通过函数名调用的操作,也能定义<<,+等各种运算符在该类对象上的新含义(即运算符重载)。
string对象上的操作

os<<s;  //将s写出输出流os中,返回os
is>>s;   //从is中读取字符串赋给s,字符串以空格分隔,返回is
getline(is,s); //从is中读取一行给s,返回is
s.empty(); 
s.size();
s.find();   //如果存在,find返回字母所在位置的下标,若不存在,返回npos
s[n]     //返回s中第n个字符的引用,位置n从0计起
s1+s2 //返回两个字符串连接后的结果
s1=s2 //用s2的副本代替s1中原来的字符
s1==s2 
s1!=s2  //对大小写敏感
< <= >  >= 利用字符在字典中的顺序进行比较,对字母的大小写敏感

注意:<<和>>都是返回运算符左侧的对象
问题1:既然string的运算符>>只能以空格分隔,如何获取一串用逗号隔开的数据?
将所有数据作为整个字符串读入,替换字符串中的逗号为空格,借助stringstream将分割用空格分隔的字符串
代码如下:

#include <string>
#include <iostream>
#include <sstream>
#include <vector>

using namespace std;

int main(int argc, char* argv[])
//当命令行需要向main函数传递数组时使用
//argc是一个形参,表示数组中字符串的数量
//argv是一个数组,其元素是指向c风格的字符串的指针
//也可以写成:int main(int argc, char** argv){}
{
	int temp;
	string strTemp;
	vector <int>array;
	int i = 0;
	stringstream sStream;
    //从命令行获取输入,全部存入字符串strTemp
	cin >> strTemp;
	
	//将strTemp中的逗号全部用空格代替
	int pos = strTemp.find(','); //如果存在,find返回字母所在位置的下标,若不存在,返回npos
	while (pos != strTemp.npos)
	{
		strTemp = strTemp.replace(pos, 1, 1, ' ');  //将字符串中的','用空格代替
		pos = strTemp.find(',');
	}

    //将替换后的字符串导入stringstream流中,stringstream可用于分割被空格、制表符等符号分割的字符串
	sStream << strTemp;  
	
	//使用>>依次读取以空格分隔的元素
	while (sStream >> temp)
	{
		array.push_back(temp);
	}
	
	for(int i=0;i<array.size();i++)
	  cout<<array[i]<<" ";
	cout<<endl;
	return 0;
}

和内置类型一样,可以使用标准库的iostream来读写string对象。在读string对象时,自动忽略开头的空格,从第一个真正的字符开始读起,直至遇见下一个空格。
由于<<和>>是返回运算符左侧的运算对象作为其结果,因此可以多个输入或者多个输出连写一起。

string s1,s2;
cin>>s1>>s2; //把第一个输入读到s1中,第二个输入读到s2中
cout<<s1<<s2; //第一个输出的是s1,第二个输出的是s2

读取未知数量的string:

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

使用getline读取一整行:当希望最终得到的字符串中保留输入时的空白符,应该使用getline代替>>,getline的参数是一个输入流和一个string对象,getline以换行符为结束的标志,如果一开始就是换行符,则读入为空字符串。getline的返回值也是它的流参数。

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

getline(is,s)和empty()结合使用,按行读取元素,并且只输出不为空的行

int main()
{
  string line;
   while(getline(cin,line))
   {
       if(!line.empty())
       cout<<line<<endl;
   }
   return 0;
}

string::size_type类型
string的size()函数返回的是string::size_type类型。
标准库类型会定义一些配套类型,体现了标准库类型与机器无关的特性。
size_type的具体实现细节不清楚,但是可以确定的是size_type是一个无符号类型。由于不清楚size_type的具体实现,所以c++11允许使用auto或者decltype来推断变量类型。
size_type是一个无符号数,不要与有符号数混用
string类型比较的原则:
①逐一比较字符;
②大小写敏感;
③如果字符串的长度不同,且较短string对象的每个字符都与较长string对象对应位置字符相同,较短string对象小于较长string对象。
字符字面值和string对象:“+”运算符只在string中重载成为可以连接两个字符串的运算符,所以使用“+”时必须保证两侧的运算对象至少有一个是string。以下语句是错误的:

string s3="rain"+"today";

c++中的字符串字面值和string是不同的类型。

3.2.3 处理string对象中的字符

改变某个字符的特性cctype头文件

isalnum(c); //当c为字母或者数字时为真
isalpha(c); //当c为字母时为真
iscntrl(c); //当c为控制字符时为真
isdigit(c); //当c为数字是为真
isxdigit(c); //当c为十六进制数字是为真
islower(c); //当c为小写字母时为真
isupper(c); //当c为大写字母时为真
isprint(c); //当c时可打印字符时为真
ispunct(c); //当c是标点符号时为真
isspace(c); //当c是空格时为真
tolower(c); //当c为大写字母时,输出对应的小写字母;否则原样输出
toupper(c); //当c为小写字母时,输出对应的大写字母;否则原样输出

c++ 标准库兼容c语言标准库,cctype是c++版本的c标准库头文件,是c++从c中沿袭下来的头文件。c++中从c沿袭的头文件在命名方面会去掉末尾“.h”,在前面加上‘c’,标识其从c语言标准库沿袭。
如果是对每一个字符进行处理,可以考虑使用范围for语句,范围for语句遍历给定序列中的每个元素并对序列中的值执行操作。
for(基础元素:序列)
执行操作
例如:

string s("string");
for(auto c:s)
  if(ispunc(c))
     cout<<c<<endl;

如果要使用范围for语句修改string类型的字符,必须把循环变量定义为引用
例如:把string对象转换成大写形式

string s("string");
for(auto &c:s)
  c=toupper(c);
cout<<s<<endl;

如果只是对string类型的部分字符进行操作,可以考虑使用下标运算符(从0开始,注意下标的合法性,上限是size()-1)或者迭代器
使用下标访问空string也会引发不可预知的结果。因此在使用下标访问之前,首先需要判定string是否为空。
为了安全,借助decltype总是
设下标的类型是string::size_type无符号型
decltype(s.size()) index=0;
c++编译器并不检查下标是否合法,所以需要程序员在写程序时格外注意。
标准库容器都是类模板,在使用时都需要指定类型。string比较特殊,因为其是字符序列,所以其类型已经确定是字符,不是类模板,不需要指定类型。
迭代器类似指针类型,迭代器指向某个元素,可以从一个元素移向另一个元素,有有效和无效之分,可以通过解引用运算符来获取某个元素。begin()返回指向第一个元素的迭代器,end()返回最后一个元素下一个位置的迭代器。若容器为空,begin和end都返回尾后迭代器。**不知道也无需知道迭代器的准确类型,**可以使用auto。在使用迭代器时,for循环中尽量使用!=而不是<,因为所有容器都定义了!=,但不是所有容器都定义了<。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值