-
C++标准库类型包括:string,vector和迭代器,其中string是可变长的字符序列,vector存放的是某种给定类型对象的可变长序列,迭代器是string和vector的配套类型,常被用于访问string中的字符和vector中的元素。
- 内置类型是C++直接定义的,与硬件密切相关,而标准库类型是一种高级类型,尚未实现到硬件中。如:相较于标准库类型string和vector,数组在灵活性上稍显不足。
3.1 命名空间的using声明
- std::cin的理解:“::”表明编译器应从操作符左侧名字所示的作用域中寻找右侧那个名字,故std::cin意味使用命名空间std中的名字cin。为了在编写程序时简短输入,使用using声明,形式为:using namespace ::name;
-
每个名字都需要独立的using声明,C++语言形式自由,既可以只有一条using声明语句,也可以多条,但要保证用到的每个名字都必须有自己的声明语句。
-
头文件不应包含using声明,因为头文件的内容会拷贝到所有引用它的文件中,当头文件中含有using声明时,每个使用该头文件的文件都含有此条声明,对于某些程序,不经意包含一些名字,可能会发生名字冲突。
练习3.1答案
1.9 while循环实现50到100的正数相加
#includeusing std::cout
using std::endl;
int main()
{
int sum=0,i=50;
while(i<=100)
{
sum+=i;
i++;
}
cout<
return 0;
}
1.10 逆序打印10到0之间的整数
1.11 键入两整数,打印两整数间的所有整数
2.41 略
3.2 标准库类型string
- string是可变长的字符序列,使用string类型必须首先包含string头文件,string定义在命名空间std中。
- string的初始化
string s1; // 默认初始化,s1是一个空字符串
string s2 = s1; // s2是s1的副本
string s3 = "hiya"; // s3是该字符串字面值的副本
string s4(10, ' c '); // s4的类容是cccccccccc - string s1 // 默认初始化,s1是一个空串
- string s2(s1) // s2是s1的副本
- string s2 = s1 // 等价于s2(s1),s2是s1的副本
- string s3("value") // s3是字面值“value"的副本,除了字面值最后的那个空字符外
- string s3 = "value" // 等价于s3("value"),s3是字面值”value"的副本
- string s4(n, ' c') // 把s4初始化为由连续n个字符c组成的串
- 使用等号初始化实际上是拷贝初始化,编译器直接把等号右侧的初始值拷贝到新的对象上,想反,不含等号的初始化是直接初始化化
string s5 = "hiya"; //拷贝初始化
string s6("hiya"); //直接初始化
string s7(10, ' c'); //直接初始化 - 用拷贝初始化对多个值进行初始化时,须创建一个临时对象用于拷贝
string s8 = string(10, 'c'); //可读性差
等价于如下:
string temp(10, 'c');
string s8 = temp; - 一个类除规定初始化其对象方式外,还需定义在对象上所能执行的操作
- 有关string对象上的操作如下表:
os< 将s写到输出流os当中,返回os is>>s 从is中读取字符串赋给s,字符串以空白分隔,返回is getline(is, s) 从is中读取一行赋给s,返回is s.empty() s为空赋返回true,否则返回false s.size() 返回s中的字符的个数 s[n] 返回s中第n个字符的引用,位置n从0计起 s1+s2 返回s1和s2连接后的结果 s1=s2 用s2的副本代替s1中原来的字符 s1==s2 如果s1和s2中所含的字符完全一样,则它们相等,返回true s1!=s2 如果s1和s2中所含的字符不一样,返回true <, <=, >, >= 利用字符在字典中的顺序进行比较,且对字母的大小写敏感 - 读写string,输入cin >> s;执行读取操作时,string对象会自动忽略开头的空白(空格符、换行符、制表符等)并从第一个真正的字符开始读起,直到下一处空白为止。
输入 cin >> s1 >> s2 >>……;执行读取多个输入 - 读取数量未知数量的string对象
#include
#include
using namespace std;
int main()
{
string word;
while (cin >> word) // 反复读取,直到文件末尾(Windows以Crtl+Z表示结束符)
cout << word << endl;
return 0;
} - 使用getline读取一整行,getlin函数的参数是一个输入流和一个string对象,函数从给定的输入流读入内容,直到换行符(包括换行符)为止,然后将所读内容拷贝到string的对象中去(此时不包含换行符), getline只要一遇到换行符就结束读取操作并返回结果,当开始输入的就是换行符,那么换回的结果就是个空的string。
#include
#include
using namespace std;
int main()
{
string line;
while (getline(cin, line)) // 每次读如一整行,直到文件末尾(Windows以Crtl+Z表示结束符)
cout << line << endl;
return 0;
} - empty是string的一个成员函数,可以使用点操作符调用empty函数
// 每次读入一整行,遇到空行直接跳过 #include
#include
using namespace std;
int main()
{
string line;
while (getline(cin, line)
if (!line.empty())
cout << line << endl;
return 0;
}
- 同样,size也是string的一个成员函数
//每次读入一整行,输出其中超过80个字符的行
#include
#include
using namespace std;
int main()
{
string line;
while (getline(cin, line)
if (line.size() > 80)
cout << line << endl;
return 0;
} - 在调用size成员函数时,返回的是一个string::size_type类型的值,是一个无符号类型的值且能够存放任何string对象的大小,所有用于存放string类的size函数返回值的变量,都应是string::size_type类型,在C++11中,通过auto或decltype推断变量的类型:
auto len = line.size(); // len的类型是string::size_type
注意在表达式中不能混用带符号数与无符号数(将会产生意想不到的结果),例如n是一个具有负值的int,则表达式s.size()- 如果在一条表达式中已经有了size()函数就不要在使用int了,可以避免因混用int与unsigned可能带来的问题
- string对象相等意味它们的长度相同且所含字符也全都相同,关系运算符<、<=、>、>=分别检验一个string对象是否小于、小于等于、大于、大于等于另外一个string对象。检验顺序按照字典顺序:
(1)若两个string对象长度不同,且较短string对象的每个字符与较长string对象对应位置上的字符相同,称较短string对象小于较长string对象。
(2)如果两个string对象在某些对应位置上的不一致,则string对象比较的结果为string对象中第一对相异字符比较的结果
示例:
string str = "Hello";
string phrase = "Hello World";
string slang = "Hiya";
可知:str < phrase"; slang > phrase > str.- string对象的赋值,string类允许把一个对象的值赋给另外一个对象:
string st1(10, 'c'),st2; //st1内容是cccccccccc; st2是一个空串
st1 = st2; //st1与st2都是空串- 两个string对象得到新的string对象,该对象内容是把左侧运算对象与右侧运算对象串拼接而成。
string s1 = "hello, ", s2 = "world\n";
string s3=s1 + s2; // s3的类容是hello, world\n
s1 += s2; //等价于s1=s1+s2- 即使一种类型并非所需,我们也可以使用它,前提是该种类型可以自动转换成所需类型,标准库允许把字符字面值和字符串字面值转换为string对象,在需要string对象的地方可以使用以上两种字面值代替
string s1 = "hello", s2 = "world"; // 在s1和s2中都没有标点符号
string s3 = s1 + ", " + s2 + '\n';
当把string对象与字面值及字符串字面值混合使用时,须确保每个加法运算符的两侧的运算对象至少有一个string
string s4 = s1 + ", "; // 正确:string对象与字面值相加
string s5 = "hello" + ", "; // 错误:两个运算对象都不是string
string s6 = s1 + ", " + "world"; // 正确:每个加号都有一个对象是string,
等价于string s6 = (s1 + ", ") + "world";
等价于:string tmp = s1 + ", "; s6 = tmp + "world";
string s7 = "hello" + ", " + s2; // 错误:不能把字面值直接相加
等价于string s7 = ("hello" + ", ") + s2; //错误:不能把字面值直接相加- 因为历史原因,C++中的字符串字面值不是标准库类型string的对象,切记,字符串字面值与string是不同的类型
练习3.2:编程以实现从标准输入中一次读入一整行;然后修改程序使其一次读入一个词。
//从标准输入中一次读入一整行
int main()
{
string line;
cout << "请输入一行文字,包括空格:" << endl;
while(getline(cin,line))
cout << line << endl;
system("pause");
return 0;
}
//一次读入一个词
int main()
{
string word;
cout << "请输入文字,不含空格:" << endl;
while(cin >> word)
cout << word << endl;
system("pause");
return 0;
}
练习3.3:说明string类的输入运算符和getline函数分别是如何处理空白字符的
答:string类输入运算符自动忽略空白字符(空格符、换行符、制表符等)并从第一个真正的字符开始读起,直到下一处空白为止,而getline函数从给定的输入流读入内容,直到换行符(包括换行符)为止,但换行符最后并不存储在最后的字符串中。
联系3.4:编程读入两个字符串并比较器是否相等并输出结果,若不等,输出较大的字符串,编程改写以上程序,比较输入的两个字符串是否等长,若不等长,输出长度交大的字符串。
// 读入两个字符串并比较器是否相等并输出结果,若不等,输出较大的字符串
#include
#include
using namespace std;
int main()
{
string st1,st2;
cout << "请输入两个字符串" << endl;
cin >> st1 >> st2;
if(st1 == st2)
cout << "两个字符串相等" << endl;
if(st1 != st2)
{
if(st1 < st2)
cout << "较大的字符串为:" << st2 < else cout << "较大的字符串为:" << st1 < } system("pause"); return 0; } #include // 从标准输入中读入多个字符串并将它们连接在一起,输出连接成的大字符串 // 使用范围for语句将字符串内的所有字符用X代替 答: 就3.6而言,不会产生任何影响,因为auto推断符同样是char型。 // 用while循环重写练习3.6 答:非法,使用下标访问空string会引发不可预知的后果,该程序的作用是通过下标输出string对象s中的第一个字符。 // 读入一个包含标点符号的字符串,将标点符号去除后输出字符串的剩余部分 答:合法,c的类型是常量引用,不能通过c修改其绑定的对象。 初始化vector对象的常用方法 列表初始化vector对象的元素:用花括号括起来的0个或多个初始元素值被赋给vector对象——>vectro 列表初始化与默认初始化在大多数情况下等价使用,但又如下例外情况: 创建指定数量的元素:用vector对象容纳的元素数量和所有元素的统一初始值来初始化vector对象: 使用花括号与圆括号区分列表初始值与元素数量: 答:(a)正确,定义了名为ivec的vector对象,其中每个元素都是vector 答:(a) v1包含1个元素,初值为0;(b) v2包含10个元素,每个元素的初值为0;(c) v3包含10个元素,每个元素的值为42;(d) v4包含1个元素,初值为10;(e) v5包含2个元素,元素初值分别是10和42;(f) v6包含10个元素,每个元素默认出事化为空串,(g) v7包含10个元素,每个元素的值都为"hi"; #include #include
// 编程改写以上程序,比较输入的两个字符串是否等长,若不等长,输出长度交大的字符串
#include
using namespace std;
int main()
{
string st1,st2;
cout << "请输入两个字符串" << endl;
cin >> st1 >> st2;
if(st1.size() == st2.size())
cout << "两个字符串长度相等" << endl;
if(st1.size() != st2.size())
{
if(st1.size() < st2.size())
cout << "较长的字符串为:" << st2 <
cout << "较长的字符串为:" << st1 <
system("pause");
return 0;
}
练习3.5:编程实现从标准输入中读入多个字符串并将它们连接在一起,输出连接成的大字符串,然后修改上述程序,用空格把输入的多个字符串分隔开来。
#include
#include
using namespace std;
int main()
{
string st1,st2;
cout << "请输入多个字符串" << endl;
while(cin >> st1)
{
st2 += st1;
}
cout << "拼接的字符串为:" << st2 <
return 0;
}
在以上程序中,结束输入时必须在键盘上输入文件结束符(windows为CRTL+Z,UNIX与MAC OS X为CRTL+D),否则程序进入了死循环,这样编写不利于人性化(总有人不知道结束符),推荐如下:
#include
#include
using namespace std;
int main()
{
string st1,st2;
char cnt = 'y';
cout << "请输入一个字符串" << endl;
while(cin >> st1)
{
st2 += st1;
cout << "是否继续(y or n)" << endl;
cin >> cnt;
if(cnt == 'y'|| cnt == 'Y')
cout << "请输入下一个字符串" << endl;
else
break;
}
cout << "拼接的字符串为:" << st2 <
return 0;
}
// 修改上述程序,用空格把输入的多个字符串分隔开来
#include
#include
using namespace std;
int main()
{
string st1,st2;
char cnt = 'y';
cout << "请输入一个字符串" << endl;
while(cin >> st1)
{
st2 += st1;
st2 += " ";
cout << "是否继续(y or n)" << endl;
cin >> cnt;
if(cnt == 'y'|| cnt == 'Y')
cout << "请输入下一个字符串" << endl;
else
break;
}
cout << "拼接的字符串为:" << st2 <
return 0;
}
3.2.3 处理string对象中的字符
isalnmu(c)
当c是字母或数字为真
isalpha(c)
当c是字母为真
iscntrl(c)
当c是控字符时为真
isdigit(c)
当c是数字为真
isgraph(c)
当c不是空格但可打印为真
islower(c)
当c是小写字母为真
isprint(c)
当c是可打印字符为真(即c是空格或c具有可视形式)
ispunct(c)
当c是标点符号为真(即c不是控字符、数字、字母、可打印空白中的一种)
isspace(c)
当c是空白为真(即c是空格、横向制表符、纵向制表符、回车符、换行符、进制符中一种)
issupper(c)
当c是大写字母为真
isxdigit(c)
当c是十六进制数字为真
tolower(c)
若c是大写字母,输出对应小写字母;否则原样输出c
toupper(c)
若c是小写字母,输出对应大写字母;否则原样输出c
// 用范围for语句在每行输出string对象中的字一个符(使用范围for语句遍历给定序列的每个元素)
#include
#include
using namespace std;
int main()
{
string str("some string");
for (auto c : str) // 对于str中的每个字符 c的类型是char
cout << c <
}
该循环还可以读作对于字符串str中的每个字符c,
// 用范围for语句和ispunct函数统计string对象中标点符号的个数(使用范围for语句遍历给定序列的每个元素)
#include
#include
using namespace std;
int main()
{
string s("Hello World!!!");
decltype(s.size()) punct_cnt = 0; //punct_cnt的类型同s.size(),即为string :: size_type
for (auto c : s) // 对于s中的每个字符
if (ispunct(c)) // 如果该字符是标点符号
++ispunct_cnt; // 计数
cout << punct_cnt << " punctuation characters in " << s << endl;
return 0;
}
// 用范围for语句将字符串改写为大写字母的形式(使用范围for语句改变字符串中的字符)
#include
#include
using namespace std;
int main()
{
string s ("Hello World!!!")
// 转换成大写形式
for (auto &c : s) // 对于s中的每个字符(c是引用)
c = toupper(c); // c是一个引用,赋值语句改变了c绑定的字符的值,标准库函数toupper将小写的参数c改为大写
cout << s << endl;
return 0;
}
// 将字符串首字符改写为大写形式
#include
#include
using namespace std;
int main()
{
string s("some string");
if (!s.empty()) // 确保s[0]的位置确实有字符
s[0] = toupper(s[0]); // 为s的第一个字符赋值
cout << s << endl;
return 0;
}
程序输出结果为:Some string
// 将字符串中的第一个词改为大写形式 (使用下标执行迭代)
#include
#include
using namespace std;
int main()
{
string s("some string")
for(decltype(s.size()) index = 0; index != s.size() && !isspace(s[index]); ++index) // 只有在index达到s.size()之前才会执行s[index],随着index的增加,它永远也会超过s.size(),确保了index比s.size()小
s[index] = toupper(s[index]);
程序输出结果为:SOME string
// 把0到15之间的十进制数转换为对应的十六进制形式
#include
#include
using namespace std;
int main()
{
const string hexdigits = "0123456789ABCDEF"; // 可能的十六进制数字
cout << "Enter a series of numbers between 0 and 15 separated by spaces. Hit ENTER when finished: " << endl;
string result; // 保存十六进制的字符串
string :: size_type n; // 用于保存从输入流读取的数(因为string下标为string :: size_type类型的值)
while (cin >> n)
if (n < hexdigits.size()) // 忽略无效输入
result += hexdigits[n]; // 得到对应的十六进制数字
cout << "Your hex number is: " << result <
}
在上述程序中,把hexdigits声明为常量是因为在后面的程序不打算在改变它的值;除应检查字符串下标的合法性外,在实际使用时,还需要检查n是否小于hexdigits的长度。 练习3.6:编写程序,使用范围for语句将字符串内的所有字符用X代替
#include
#include
using namespace std;
int main()
{
string s;
cout << "请输入一个字符串,可以含空格" << endl;
getline(cin,s);
for(auto &c : s)
c='X';
cout << s <
return 0;
}
练习3.7:就3.6而言,若将循环控制变量的类型设为char将发生什么?先估计,在编程进行验证
练习3.8:分别用while循环和传统的for循环重写第一题的程序,判断哪种形式更好,说明原因。
#include
#include
using namespace std;
int main()
{
string s;
int i = 0;
cout << "请输入一个字符串,可以含空格" << endl;
getline(cin,s);
while(s[i] != '\0') //'\0'为字符串结束符
{
s[i] = 'X';
++i;
}
cout << s <
return 0;
}
// 用for循环重写练习3.6
#include
#include
using namespace std;
int main()
{
string s;
cout << "请输入一个字符串,可以含空格" << endl;
getline(cin,s);
for(string::size_type i = 0;i < s.size(); ++i)
s[i] = 'X';
cout << s <
return 0;
}
练习3.9:判断程序是否合法,有何作用,说明原因
练习3.10:编程实现读入一个包含标点符号的字符串,将标点符号去除后输出字符串的剩余部分
// 使用范围for
#include
#include
using namespace std;
int main()
{
string s,result;
cout << "请输入一个字符串,包含标点符号" << endl;
getline(cin,s);
for(auto c : s)
if(!ispunct(c))
result += c;
cout << result <
return 0;
}
//使用普通for
#include
#include
using namespace std;
int main()
{
string s,result;
cout << "请输入一个字符串,包含标点符号" << endl;
getline(cin,s);
for(decltype(s.size()) i = 0; i < s.size(); ++i)
if(!ispunct(s[i]))
result += s[i];
cout << result <
return 0;
}
练习3.11:判断语句是否合法,若合法,说明c的类型
3.3 标准库类型vector
以vector为例,提供的额外信息是vector内所存对象的类型
vector
vector
vector3.3.1定义和初始化vector对象
vector
v1是一个空vector,它潜在的元素是T型的,执行默认初始化
vector
v2中包含v1所有元素的副本
vector
等价于v2(v1),v2中包含有v1所有元素的副本
vector
v3包含了n个重复的元素,每个元素的值都是val
vector
v4包含了n个重复地执行了值初始化的对象
vector
v5包含了初始值个数的元素,每个元素被赋予相应的初始值
vector
等价于v5{a,b,c…}
vector
vector
vector
vector
vector
(1) 使用拷贝初始化(使用=)时,只能提供一个初始值;
(2) 如果提供的是一个类内初始值,只能使用拷贝初始化或使用花括号的形式初始化;
(3) 如果提供的是初始元素值的列表,则只能把初始值都放在花括号里进行列表初始化,而不能放在圆括号里
vector
vector
vector
vector
vector
vector
在后两种初始化方式当中使用了值初始化(只提供vector对象容纳元素的数量而省略初始值,此时库会创建一个值初始化的元素初值并把它赋给容器中的所有元素)的方式,但这种方式受到两个限制:
(1)一些类要求必须明确地提供初始值,若vector对象中的元素类型不支持默认初始化,就必须提供元素的初始值
(2)只提供元素的数量而无初始值,只能使用直接初始化:
vector
vector
vector
vector
vector
通过以上的初始化我们可以看出,使用圆括号所提供的值用来构造vector对象,使用花括号为列表初始化vector对象,但是当使用花括号的形式但是提供的值又不能用来列表初始化,需考虑这样的值来构造vector对象
vector
vector
vector
vector
在以上4个例子中,只有v5是列表初始化,也就是说进行列表初始化vector对象时,花括号里的值必须与元素类型相同,显然不能用int初始化string对象,所以v7,v8提供的值不能作为元素的初始值,确认无法列表初始化后,编译器会尝试用默认值初始化vector对象练习3.12:下列vector对象的定义不正确的地方吗?若有,请指出来,对于正取的,描述其执行结果;对于不正确的,说明其错误原因。
(a) vector
(b) vector
(c) vector练习3.13:下列的vector对象个包含多少个元素?这些元素的值分别是多少?
(a) vector
(c) vector
(e) vector
(g) vector
3.3.2 向vector对象添加元素
//向vector中添加0到99的元素
vector
for (int i = 0; i != 100; ++i)
v2.push_back(i); //依次把整数值放到v2尾端
//循环结束后v2有100个元素,值从0到99
在上例中,一开始还是把vector声明为空vector,在每次迭代时才顺序地把下一个整数作为v2的新元素添加给vector
//从表中输入中读取单词,将其作为vector对象的元素存储
string word;
vector
while (cin >> word) {
text.push_back(word);
}
在上例中,先创建一个空vector,之后依次读入未知数量的值并保存到text中。练习3.14:编写程序,用cin读入一组整数并把它们存入一个vector对象。
#include
using namespace std;
int main()
{
int num;
char cont = 'y';
vector
while(cin >> num)
{
ivec.push_back(num);
cout << "还要继续输入吗?(y或者n)"<
if(cont != 'Y'&&cont != 'y')
break;
}
for(auto mem : ivec) //使用范围for循环遍历ivec中的每个元素
cout << mem << " ";
cout << endl;
system("pause");
return 0;
}练习3.15:改写上述程序,不过这次读入的是字符串
#include
#include
using namespace std;
int main()
{
string str;
char cont = 'y';
vector
while(cin >> str)
{
svec.push_back(str);
cout << "还要继续输入吗?(y或者n)"<
if(cont != 'Y'&&cont != 'y')
break;
}
for(auto mem : svec)
cout << mem << " ";
cout << endl;
system("pause");
return 0;
}3.3.3 其他vector操作