目录
第三章
一、命名空间的using声明
二、标准库的string类型
1、string对象的定义和初始化
2、string对象的读写
3、string对象的操作
4、string对象中字符的处理
三、标准库的vector类型
1、vector对象的定义和初始化
2、vector对象的操作
四、迭代器简介
1、简介
2、begin和end操作
3、vector迭代器的自增与解引用运算
4、==或!=操作符
5、应用
6、const_iterator
7、迭代器的算术操作
五、标准库bitset类型
1、用unsigned值初始化bitset对象
2、用string对象初始化bitset对象
3、bitset对象上的操作
第三章
一、命名空间的using声明
1、::是作用域操作符。含义是右操作数的名字可以在左操作数的作用域中找到。
例:std::cin是说cin是在命名空间std中定义的。
2、使用using声明可以直接引用名字,不需要再引用该名字的命名空间。
#include <string>
#include <iostream>
using std::cin;
using std::string;
int main()
{
string s;
cin>>s;
cout<<s;
std::cout<<s;
}
3、但是,在头文件中必须使用完全限定的标准库名字。并且头文件中应该只定义必要的东西。
(如果在头文件中放置using声明,相当于在包含该头文件的每个程序中都放置了同一using声明,不论该程序是否需要using声明。)
二、标准库的string类型
1、string对象的定义和初始化
构造函数是一个特殊成员函数,定义如何初始化该类型的对象。
初始化string对象的方式:
(1)string s1;默认构造函数,s1为空串
(2)string s2;将s2初始化为s1的一个副本
(3)string s3(“value”);将s3初始化为一个字符串字面值副本
(4)string s4(n,‘c’);将s4初始化为字符‘c’的n个副本
2、string对象的读写
1、使用标准输入输出读写string对象
int main()
{
string s;
cin>>s;
cout<<s<<endl;
return 0;
}
特点:
- 读取并忽略开头所有的空白字符(空格,换行符,制表符)
- 读取字符直到再次遇到空白字符,终止读取
例:输入 “ Hello World ” 会输出“Hello”不含任何空格
改写:
string s1,s2;
cin>>s1>>s2;
cout<<s1<<s2<<endl;
输入 “ Hello World ” 会输出“HelloWorld”
读取未知数目的string对象(分单词读取):
int main()
{
string word;
while(cin>>word)
cout<<word<<endl;
return 0;
}
若还未到达文件尾且未遇到无效输入,则执行while循环体。若到达了文件尾,则跳出while循环
2、用getline读取整行文本
getline函数接受两个参数:一个输入流对象和一个string对象
从输入流的下一行读取并保存读取的内容到string中,但不包含换行符
getline不忽略开头的换行符。只要getline遇到换行符,即使它是输入的第一个字符,也将停止读入并返回。如果第一个字符就是换行符,string参数将被置空。
例:分行读取
int main()
{
string line;
while(getline(cin,line))
cout<<line<<endl; //由于line不含换行符,若逐行输出需要自行添加endl
return 0;
}
3、string对象的操作
1、string对象的长度是指string对象中字符的个数,可通过size操作获取
判断string对象是否为空:
if(st.size()==0) //若为真则st为空
if(st.empty()) //若为真则st为空
2、 string::size_type类型
不要把size的返回值赋给int变量。因为string::size_type是unsigned类型,存储长度是int能存储的两倍,用int可能会溢出
为了避免溢出,保存string对象size的最安全的方法是使用标准库类型string::size_type
3、string关系操作符(==、<、>)
比较两个string对象时采用大小写敏感的字典排序策略
- 如果两个string对象长度不同,且短的string对象与长的string对象前面的部分匹配(是其前缀),则短的string对象小于长的string对象。
- 如果两个string对象字符不同,则比较第一个不匹配的字符
4、string对象的赋值
st1=st2;
先把st1占用的相关内存释放掉,然后分配给st1足够存放st2副本的内存空间,最后把st2中的所有字符复制到新分配的内存空间。
5、两个string对象相加
string s1("hello, ");
string s2("world\n");
string s3 = s1 + s2; //s3 is "hello, world\n"
如果要把s2直接追加到s1末尾,可以使用+=操作符
s1 += s2;
6、和字符串字面值的连接
当进行string对象和字符串字面值混合连接操作时,+操作符的左右操作数必须至少有一个是string类型的。
string s1 = "world";
string s2 = "hello" + ","; //不合法,+两侧都是字符串类型
string s3 = s1 + "\n" //合法,有一侧是string类型
string s4 = s1 + "," + "yes"; //合法,从左到右计算,s1 + ","算完变成string类型
string s5 = "," + "yes" + s1; //不合法,从左到右计算,两个字符串类型先计算
7、从string对象获取字符
用下标分别取出string对象的每个字符,分行输出
string str("some string");
for(string::size_type ix = 0; ix != str.size(); ix++)
{
cout << str[ix] << endl;
}
8、计算下标值
str[int1*int2] = int3;
虽然整型数值都可以作为索引,但索引的实际数据类型是unsigned类型string::size_type
4、string对象中字符的处理
可打印的字符是指那些可以显示表示的字符(包括空格,也是可显示表示的)。
空白字符是空格、制表符、垂直制表符、回车符、换行符、进纸符中的任意一种。
标点符号是除了数字、字母或(可打印的)空白字符(如空格)以外的其他可打印字符
例1:输出一给定string对象中标点符号的个数
string s("Hello World!!!");
string::size_type punct = 0;
for(string::size_type index = 0; index != s.size(); ++index)
{
if(ispunct(s[index]) ++punct;
}
cout<<punct<<endl;
//输出3
例2:把string对象s中的字母改写为小写字母
string s("Hello World!!!");
for(string::size_type index = 0; index != s.size(); ++index)
s[index] = tolower(s[index]);
cout << s << endl;
//输出hello world!!!
cname头文件中定义的名字都定义在命名空间std内,而.h版本中的名字不是这样。
通常c++程序中应该采用cname这种头文件版本,这样标准库中的名字在命名空间std中保持一致。
string s;
cout << s[0] << endl;
//声明了一个字符串 s,但没有给它赋值。因此,它是一个空字符串。
//当你尝试访问 s[0] 时,你正在尝试访问空字符串的第一个字符,这是未定义的行为(undefined behavior)。
//在C++中,尝试访问空字符串或超出字符串长度的索引通常会导致程序崩溃或产生不可预测的结果。
三、标准库的vector类型
一个容器中所有对象都必须是同一种类型的。
vector是一个类模板,而不是一种数据类型。可用来定义任意多种数据类型。如vector<int>、vector<string> 都是数据类型。
1、vector对象的定义和初始化
创建确定个数的元素:
vector<int> ivec(10,-1);
vector<string> svec(10,"hi!");
值初始化:
如果没有指定元素的初始化式,标准库将自行提供一个元素的初始值进行值初始化。
具体值是什么取决于存储在vector中元素的数据类型。
vetcor<int> fvec(10);//10个元素,每个都初始化为0
vetcor<string> svec(10);//10个元素,每个都初始化为空串
以上即,用各类型默认构造函数创建初始化式。
若元素类型是没有定义任何构造函数的类类型,标准库仍产生一个带初始值的对象,这个对象的每个成员进行值初始化。
2、vector对象的操作
1、empty和size操作类似于string类型。size返回类型为相应vector类定义的size_type的值。
vector<int>::size_type //ok
vector::size_type //error
vector类型总是包括vector的元素类型
2、向vector中添加元素
string word;
vector<string> text;
while(cin>>word)
text.push_back(word);
3、vector的下标操作
for(vector<int>::size_type ix = 0; ix != ivec.size(); ++ix)
ivec[ix] = 0;
但是,只能用于已经有元素的vector,若vector不确定有没有元素,不能用下标赋值(可能会溢出)
只能用v = ix; ivec.push_back(v)的方式赋值并添加到vector中
例题:
读入一段文本到vector对象,每个单词存储为vector中的一个元素,把vector对象中每个单词转化为大写字母。输出vector对象转化后的元素,每八个单词为一行输出。
#include <iostream>
#include <fstream>
#include <vector>
#include <cassert>
#include <string>
#include <sstream>
using namespace std;
int main()
{
ifstream in("1.txt"); //打开文件
if(!in.is_open()) cout<<"open is error"<<endl; //确认是否打开
//assert(in);
string word;
string line;
vector<string> words;
// 逐行读取文件内容
while (getline(in, line)) {
istringstream ss(line);
// 使用istringstream分割行到单词,并存入vector
while (ss >> word) {
words.push_back(word);
}
}
// 关闭文件
in.close();
int count=0;
for(vector<string>::size_type ix=0; ix != words.size(); ix++)
{
for(string::size_type j=0; j != words[ix].size(); j++)
{
words[ix][j] = toupper((unsigned char)words[ix][j]); //每个字母变大写
}
cout<<words[ix]<<" ";
count++;
if(count%8 == 0) //满八个换一行
{
cout<<endl;
}
}
if(count%8!=0) //最后一行若不满8个也换行
{
cout<<endl;
}
return 0;
}
常用的简便输出vector的方法:
vector<string> words;
for (const auto& w : words) //const是指不改变w,auto指自动识别words里存的类型
{
std::cout << w << " ";
}
关于<sstream>
sstream
是 C++ 标准库中的一个头文件,全称为 <sstream>
。它提供了对字符串流(string stream)的支持,允许你以类似于文件流(file stream)或控制台流(console stream)的方式对字符串进行操作,用于字符串的解析和转换。sstream
头文件中定义了几个类,主要包括 istringstream
、ostringstream
和 stringstream
。
istringstream
类用于从字符串中读取数据,类似于输入文件流(ifstream
)。ostringstream
类用于将数据写入字符串,类似于输出文件流(ofstream
)。stringstream
类同时支持读取和写入字符串,类似于输入输出文件流(fstream
)
四、迭代器简介
1、简介
迭代器对所有容器都适用,c++程序更倾向于使用迭代器而不是下标操作访问容器元素。
每种容器都定义了自己的迭代器类型,如vector
vector<int>::iterator iter;
2、begin和end操作
每种容器都定义了begin和end函数,用于返回迭代器。
begin返回迭代器指向的第一个元素
vector<int>::iterator iter = ivec.begin();
end操作返回的迭代器指向vector末端元素的下一个元素,即指向了一个不存在的元素
如果vector为空,begin返回的迭代器与end返回的迭代器相同
3、vector迭代器的自增与解引用运算
迭代器类型可以使用解引用操作符(*)来访问迭代器所指向的元素
例:*iter = 0;++iter;
因为end操作返回的迭代器不指向任何元素,因此不能对它进行解引用或自增操作
4、==或!=操作符
用来比较两个迭代器,如果两个迭代器对象指向同一个元素,则它们相等,否则不相等。
5、应用
for(vector<int>::size_type ix = 0; ix != ivec.size(); ++ix)
ivec[ix] = 0; //将vector中元素循环赋值为0
for(vector<int>::iterator iter = ivec.begin(); iter != ivec.end(); ++iter)
*iter = 0; //用迭代器改写
如果vector为空,程序也是安全的。因为ivec为空的话,begin返回的迭代器不指向任何元素,从begin操作返回的迭代器和从end操作返回的迭代器值相同,因此for语句中测试条件立即失败。
6、const_iterator
每种容器类型还定义了一种名为const_iterator的类型,该类型只能用于读取容器内元素,但不能改变其值。
for(vector<int>::const_iterator iter = ivec.begin(); iter != ivec.end(); ++iter)
cout<<*iter<<endl; //只能用,不能改变其指向对象的值
使用const_iterator类型时,迭代器自身的值可以变(可以自增或解引用),但不能改变其指向的元素的值。
不要把const_iterator和const的iterator混淆
const的iterator是指const类型的iterator迭代器。声明一个const迭代器时,必须初始化迭代器,且一旦初始化后,就不能改变它的值(不能自增或解引用,即不能让它指向其他元素),但可以改变它指向的元素的值。
vector<int> nums(10);
const vector<int>::iterator cit = nums.begin();
*cit = 1;//可以改变它指向的元素的值
7、迭代器的算术操作
iter + n
iter - n
iter1 - iter2
vector<int>::iterator mid = vi.begin() + vi.size()/2; //比遍历到中间快
任何改变vector长度的操作都会使已存在的迭代器失效。比如调用push_back()后就不能再信赖指向vector迭代器的值了。
五、标准库bitset类型
bitset类是一种类模板
与vector不同的是,bitset类型对象的区别仅在于长度,不在于类型。
定义bitset时,要明确bitset含有多少位,需要在尖括号内给出它的长度值。
bitset的构造函数:
1、用unsigned值初始化bitset对象
在32位unsigned long的机器上,十六进制值0xffff表示为二进制位就是16个1和16个0
bitset<16> bitvec1(0xffff); //少于unsigned long的位数,初始值的高阶位被丢弃
//bits 0……15 are set to 1
bitset<32> bitvec2(0xffff); //与unsigned long的位数相同,正好放置初始值
//bits 0……15 are set to 1;16……31 are 0
bitset<128> bitvec3(0xffff); //长度大于32,31位以上的高阶位被置0
//bits 32 through 127 initialized to zero
2、用string对象初始化bitset对象
string strval("1100");
bitset<32> bitvec4(strval);
//bitvec4的位模式中第2和第3的位置为1,其余位置为0.如果string对象的字符个数小于bitset类型的长度,则高阶位将置为0。
string对象和bitset对象之间是反向转化的。string对象最右边的字符(下标最大的字符)用来初始化bitset对象的低阶位(下标为0的位)
也可以只用string对象的子串作为bitset对象的初始值。
bitset<32> bitvec5(str,5,4); //从str[5]开始,往后4位
bitset<32> bitvec6(str, str.size() - 4); //最后4位
3、bitset对象上的操作
(1)count操作的返回类型是标准库中命名为size_t的类型。size_t类型定义在cstddef头文件中。该文件是C标准库的头文件stddef.h的C++版本。
(2)bitset的size操作返回bitset对象中二进制位的个数,返回值的类型是size_t
(3)访问bitset对象中的位可以用下标,也可以用下标测试某位的值或设置某位的值
for(int index = 0;index !=32; index += 2)
{
bitvec[index] = 1;
}
(4)flip操作
bitvec.flip(0);//bit的第一位取反
bitvec[0].flip();//同上
bitvec.flip();//bit的所有位取反
(5)获取bitset对象的值
当bitset类型的长度<=unsigned long的长度时,才可以使用to_ulong操作。
bitset<32> bitvec2(0xffff);
unsigned long ulong = bitvec3.to_ulong();
cout << "ulong = " << ulong << endl;
输出 65535。
在十六进制数0x0000ffff
中,每个十六进制位代表4个二进制位。则0x0000ffff
的低16位都是1
转化为10进制:1 * 1 + 1 * 2 + 1 * 4 + 1 * 8 + 1 * 16 + 1 * 32 + 1 * 64 + 1 * 128 + 1 * 256 + 1 * 512 + 1 * 1024 + 1 * 2048 + 1 * 4096 + 1 * 8192 + 1 * 16384 + 1 * 32768 = 65535
(6)输出二进制位
bitset<32> bitvec2(0xffff);
cout << "bitvec2: " << bitvec2 <<end;