C++ primer 5th 第5章 语句
=====================================================================
第5章 语句 154~159页 简单语句
=====================================================================
//QQ108201645编写
#include <iostream>
#include <vector>
#include <string>
using namespace std;//使用命名空间
#ifndef DECLARATION
#define DECLARATION
//说明部分
//执行已定义声明的话将不再执行
#endif
int get_num()
{
return 0;
}
int main()
{
int ival = 0;
ival + 5;//一条没什么用处的表达式语句
cout<<endl;//一条有用的表达式语句
//空语句
;
int s, sought = 5;
while (cin>>s&&s!=sought)//循环输入,直到文件末尾或某次输入等于sought
{
;//空语句
}
//复合语句
int val = 0, sum = 0;
while (val<=10)
{
sum += val;//把sum+val的值赋给sum
++val;//把val加1
}
//空块,指内部没有任何语言的一对花括号,等价于空语句
//临时声明函数
while (int i = get_num())//每次迭代时创建并初始化i
{
cout << i << endl;
}
//i = 0;//错误:在外循环无法访问i
vector<int>v(10, 10);
auto beg = v.begin();
while (beg!=v.end()&&*beg>=0)
{
++beg;
}
if (beg == v.end())
;//此时我们知道v中的所有元素都大于等于0
#ifndef DECLARATION
条件语句
if (条件) 如果condition为真,执行说明(statement)1部分, 如果条件为假跳过说明1,从else开始执行
说明1;
else 如果condition为假, 执行说明2部分,如果代码较长,要用大括号
说明2;
#endif
const vector<string> score = { "F","D","C","B","A","A++" };
//我们使用if else根据成绩是否合格执行不同操作
//如果grade小于60,对应字母是F ,否则计算其下标
int grade = 60;
string lettergrade;
if (grade < 60)//判断grade的值是否小于60,根据结果执行是if还是else分支
lettergrade = score[0];
else
{//如果执行else把grade减50,然后执行整除法,得到字母的位置
lettergrade = score[(grade - 50) / 10];
if (grade!=100)
if (grade % 10 > 7)
lettergrade += '+';//末尾是8或9的成绩添加一个加号
else if (grade % 10 < 3)
lettergrade += '-';//末尾是0、1、2的成绩添加一个减号
}//如果没有花括号代码看起来是正确的,但因为值小于60会无条件的在末尾添加减号
cout<<lettergrade<<endl;
#ifndef DECLARATION
//错误:实际执行过程并非像缩进那样,else分支匹配的是内层的if语句
if (grade % 10 >= 3)
if (grade % 10 > 7)
lettergrade += '+';//末尾是8或者9的成绩添加一个加号
else
lettergrade += '-'; //末尾是3、4、5、6、7 的成绩添加一个减号
;
上面那条意思如下
if (grade % 10 >= 3)
{
if (grade % 10 > 7)
lettergrade += '+';//末尾是8或者9的成绩添加一个加号
else
lettergrade += '-';
}
所以你必须有花括号强制else与外层匹配
if (grade % 10 >= 3)
{
if (grade % 10 > 7)
lettergrade += '+';//末尾是8或者9的成绩添加一个加号
}
else
lettergrade += '-';
#endif
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
#include <iostream>
#include <vector>
#include <string>
using namespace std;//使用命名空间
#ifndef DECLARATION
#define DECLARATION
//说明部分
//执行已定义声明的话将不再执行
#endif
int main()
{
//练习题5.5编写程序使用if else把字数成绩转换成字母成绩
const vector<string> svec = { "F","E","D","C","B","A","A++" };
string buf;
int grade;
cin >> grade;
#ifndef DECLARATION
if (grade > 60)//当大于60时执行下面。否则跳转到else (1)
{
buf = svec[(grade - 50) / 10];//把分数分成5个等级。所以减一半,除以10将得到一个个位值
if (grade != 100)//如果等于100就不执行下面的
{
if (grade % 10 >= 6)//当不等于100,再除10得到余数大于6则末尾加“+”
buf += "+";
else //当不等于100,再除10得到余数小于6则末尾加“-”
{
buf += "-";
}
}
}
else //else (1)
{
buf = svec[0];
}
cout<<buf<<endl;
#endif
//练习题5.5 更改上面程序用条件运算符
cout<<(grade>60?buf=svec[(grade-50)/10]+
(grade%10>6?"+":"-"):
buf=svec[0])<<endl;
#ifndef DECLARATION
练习题5.7:改正下列代码中的错误
if (ival != ival2)
ival = ival2;
if (ival < minval)
minval = ival;
occurs = 1;
if (int ival=get_value())
cout<<"ival = "<<ival<<endl;
if (ival = 0)
ival = get_value();
#endif
int ival = 0, ival2 = 2, minval = 3, occurs = 0;
if (ival != ival2)
ival = ival2;
if (ival < minval)
{
minval = ival;
occurs = 1;
}
int get_value();//声明临时函数
if (int ival = get_value())
cout << "ival = " << ival << endl;
if (ival == 0)
ival = get_value();
//练习题5.8什么是悬垂else
//就是一个if语句嵌套在另一个if语句内部,使得if分支多于else分支,c++规定else与离它最近的尚未匹配的if匹配
system("pause");
return 0;
}
int get_value()
{
return 0;
}
=====================================================================
第5章 语句 160页 switch语句
=====================================================================
//QQ108201645编写
#include <iostream>
#include <vector>
#include <string>
using namespace std;//使用命名空间
#ifndef DECLARATION
#define DECLARATION
//说明部分
//执行已定义声明的话将不再执行
#endif
int main()
{
unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0;
char ch='a';
while (cin >> ch)
{
switch (ch)
{
//如果ch是元音字母,将其应的计数加1
case 'a':
++aCnt;
break;///如果不加break,将跳到下一个case
case 'e':
++eCnt;
break;
case 'i':
++iCnt;
break;
case 'o':
++oCnt;
break;
case 'u':
++uCnt;
break;
}
}
cout<<"Number of vowel a: \t"<<aCnt<<'\n'
<<"Number of vowel e: \t"<<eCnt<<"\n"
<<"Number of vowel i: \t"<<iCnt<<'\n'
<<"Number of vowel o: \t"<<oCnt<<'\n'
<<"Number of vowel u : \t"<<uCnt<<endl;
unsigned vowelCnt = 0;
switch (ch)
{//如果出现a、e、i、o、u的任意一个都会将vowelCnt的值加1
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':
++vowelCnt;//只要ch是元音字母,不管到底是五个的哪一个都执行相同操作
break;
}
//另一种写法
switch (ch)
{
case 'a':case 'e':case 'i':case 'o':case 'u':
++vowelCnt;
break;
}
//如果没有任何一个case标签能匹配switch,程序紧跟着default
int otherCnt = 0;
switch (ch)
{
case 'a':case 'e':case 'i':case 'o':case 'u':
++vowelCnt;
break;
default:
++otherCnt;
break;
}
int next_num();
bool condition = 0;
#ifndef DECLARATION
switch (condition)
{
case true:
string file_name;//错误。控制流绕过一个隐式初始化的变量
int ival = 0;//错误:控制流绕过一个显式的初始化变量
int jval;
break;
case false://一旦控制流直接跳到false,同时也就略过了变量file_name和ival的初始化过程
jval = next_num();//在尚未初始化时使用它们,这显然行不通
if (file_name.empty()) { ; }//file_name在作用域内。但没被初始化
break;
}
#endif
string get_file_name();
switch (condition)
{
case true:
{
string file_name = get_file_name();//正确:声明位于语句块内部
}
break;
case false:
#ifndef DECLARATION
if (file_name.empty()) { ; }//错误:file_name不在作用域内;
#endif
;
break;
}
system("pause");
return 0;
}
int next_num()
{
return 0;
}
string get_file_name()
{
return "abc";
}
=====================================================================
第5章 语句 164页 练习
=====================================================================
#include <iostream>
#include <cctype>
#include <string>
using namespace std;//使用命名空间
#ifndef DECLARATION
#define DECLARATION
//说明部分
//执行已定义声明的话将不再执行
#endif
int main()
{
//练习题5.9编写程序,使用一系列的if语句统计从cin读入的文本中有多少个元音字母
char ch;
unsigned quantity = 0;
while (cin >> ch&&!isdigit(ch))
{
if (ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u')
++quantity;
}
//练习题5.10修改元音程序,使其能统计大小写的元音
cout<<"元音数量"<<quantity<<endl;
int aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0;
while (cin >> ch && !isdigit(ch))
{
ch = tolower(ch);
if (ch == 'a')
aCnt++;
if (ch == 'e')
eCnt++;
if (ch == 'i')
iCnt++;
if (ch == 'o')
oCnt++;
if (ch == 'u')
uCnt++;
}
cout << "Number of vowel a: \t" << aCnt << '\n'
<< "Number of vowel e: \t" << eCnt << "\n"
<< "Number of vowel i: \t" << iCnt << '\n'
<< "Number of vowel o: \t" << oCnt << '\n'
<< "Number of vowel u : \t" << uCnt << endl;
//练习题5.11修改元音程序,使其能统计空格,制表符和换行符的数量
quantity = 0;
cin >> ch;
switch (ch)
{
case 'a': case 'A': case 'e': case 'E': case 'i': case 'I': case 'o':
case 'O': case 'u': case 'U': case ' ': case '\t': case '\n':
++quantity;
}
//练习5.12修改统计元音字母的程序,使其能统计含以下两个字符的字符序列的数量: ff、fl和fi。
string s;
quantity = 0;
while (cin>>s && s!="end")
{
if (s=="ff"||s=="fl"||s=="fi")
++quantity;
}
cout<<quantity<<endl;
#ifndef DECLARATION
练习5.13:下面显示的每个程序都含有一个常见的编码错误,指出错误在哪里,然后修改它们。
;
//(a)
unsigned aCnt = 0, eCnt = 0, iouCnt = 0;
char ch = next_text();
switch (ch)
{
case 'a': aCnt++;//每个都少了break
case 'e': eCnt++;
default: iouCnt++;
}
//(b)
unsigned index = some_value();
switch (index)
{
case 1:
int ix = get_value();
ivec[ix] = index;
break;
default:
ix = ivec.size() - 1;//执行这条时标签未定义
ivec[ix] = index;
}
//(c)
unsigned evenCnt = 0, oddCnt = 0;
int digit = get_num() % 10;
switch (digit)
{
case 1, 3, 5, 7, 9://标签错误两个都要写完整case 1:case 3:case 5:case 7:case 9:
oddcnt++;//变量名错误
break;
case 2, 4, 6, 8, 10:
evencnt++;
break;
}
(d) unsigned ival = 512, jval = 1024, kval = 4096;//要改成整型常量表达式。
unsigned bufsize;
unsigned swt = get_bufCnt();
//声明一个函数return 0;就可以用
switch (swt)
{
case ival://标签必须是整型常量表达式。
bufsize = ival * sizeof(int);
break;
case jval:
bufsize = jval * sizeof(int);
break;
case kval:
bufsize = kval * sizeof(int);
break;
}
#endif
system("pause");
return 0;
}
=====================================================================
第5章 语句 165页 迭代语句
=====================================================================
//QQ108201645编写
#include <iostream>
#include <vector>
#include <string>
#include <cctype>
using namespace std;//使用命名空间
#ifndef DECLARATION
#define DECLARATION
//说明部分
//执行已定义声明的话将不再执行
#endif
int main()
{
#ifndef DECLARATION
//只要ccondition 结果为真就一直执行statement,condition不能为空,
// 如果第一次执行为false,则一次循环都不会执行
while (condition)
{
statement;
}
#endif
;
vector<int> v;
int i;
while (cin >> i&&i!=-1)
v.push_back(i);
//寻找第一个元素
auto beg = v.begin();
while (beg != v.end() && *beg >= 0)
{
++beg;
}
if (beg == v.end())
;//此时我们知道v中的所有元素都大于等于0
/*练习5.14:编写一段程序,从标准输入中读取若干string对象并查找连续重复出现的
单词。所谓连续重复出现的意思是:一个单词后面紧跟着这个单词本身。要求记录连续
重复出现的最大次数以及对应的单词。如果这样的单词存在,输出重复出现的最大次数;
如果不存在,输出一条信息说明任何单词都没有连续出现过。
例如:如果输入是:
how now now now brown cow cow
那么输出应该表明单词now连续出现了3次。*/
cout<<"---"<<endl;
string buf, sought;
int Cnt ;
if (cin>>sought)
{
Cnt = 1;
while (cin >> buf && buf != "end")
if (buf == sought)
Cnt++;
else
{
cout << sought << " " << Cnt << endl;
sought = buf;
Cnt = 1;
}
cout<<sought<<" "<<Cnt<<endl;
}
#ifndef DECLARATION
传统的for语句
for (init - statemen; condition; expression)
statement;
关键字for及括号里的部分称作for语句头
init-statement 必须是以下三种形式中的一种:声明语句,表达式语句或者空语句,
因为这些语句都以分号作为结束,所以for语句的语法形式也可以看做
for (initializer; condition; expression)
statement;
init - statement负责初始化一个值, 这个值将随着循环的进行而改变.
condition作为循环控制的条件, 只要为真, 就执行一次statement, 如果condition的第一次
求值结果就是false, 则statement一次也不会执行.expression负责修改init - statement初始化的
变量, 这个变量正好就是condition检查的对象, 修改发生在每次循环迭代之后, statement可以是一条
复合语句;
#endif
string s("abcdefg");
//重复处理s中的字符串直至结束
for (decltype(s.size()) index = 0;
index != s.size() && !isspace(s[index]); ++index)
s[index] = toupper(s[index]);//将当前字符改成大写形式
/*循环开始时, 先判断一次init - staement.此例中定义index并初始化为0;
接下来判断condition.如果index不等于s.size()而且在 s[index]位置的字符
不是空白,则执行for循环举杯 的内容.否则终止.如果第一次迭代时条件为假
,for循环一次也不会执行.
如果条件为真,执行循环体,此例中,for循环体将s[index]位置的字符改写成大写形式
最后执行expression.此例中,将index加1*/
//多重声明,记录下v的大小,当到达原来的一个元素后结束循环
for (decltype(v.size()) i = 0, sz = v.size(); i != sz; ++i)
v.push_back(v[i]);
//如果无须初始化,则我们可以使用一条空语句作为init-statement,例如,对于在vector对象中寻找第一个
//负数的程序,完全能用for循环改写
auto pbeg = v.begin();
for (/*空语句*/; pbeg != v.end() && *beg >= 0; ++beg)
;//什么也不做
//或者
for (int i=0;/*条件为空*/;++i)
{
/*对i进行处理,循环内部的代码必须负责终止迭代过程*/
}
vector<int> ivec;
cout<<"---"<<endl;
for (int i; cin >> i&&i!=-1;/*表达式为空*/)
v.push_back(i);//因为部分条件能改变i的值,所以这个循环无须表达式部分,其中,条件部分
//不断检查输入流的内容,只要读取所有的输入或者遇到一个输入错误就终止循环
system("pause");
return 0;
}
=====================================================================
#include <iostream>
#include <vector>
#include <string>
#include <cctype>
#include <cstddef>
using namespace std;//使用命名空间
#ifndef DECLARATION
#define DECLARATION
//说明部分
//执行已定义声明的话将不再执行
#endif
int main()
{
#ifndef DECLARATION
//练习5.15:说明下列循环的含义并改正其中的错误。
(a) for (int ix = 0; ix != sz; ++ix) { /* ... */ }
if (ix != sz)
// . . .;
(b) int ix;
for (ix != sz; ++ix) { /* ... */ }
(c) for (int ix = 0; ix != sz; ++ix, ++sz) { /*...*/ }
解答a
vector<int > ivec{ 1,2,3 };
int sz = ivec.size(),ix=0;//这里应该定义ix变量,if部分才能访问
for (int ix = 0; ix != sz; ++ix) { /* ... */ }
if (ix != sz)
;
解答b
int ix;
for (ix != sz; ++ix) { /* ... */ }
//for循环中缺少一个init-statement的分号,就算是空语句也要分号
解答c
for (int ix = 0; ix != sz; ++ix, ++sz) { /*...*/ }
//sz自增的话,将会无限循环下去,因为ix永远也不会与sz相等就达不到终止循环的条件
#endif
/*练习5.17:假设有两个包含整数的vector对象,编写一段程序,检验其中一个vector
对象是否是另一个的前缀。为了实现这一目标,对于两个不等长的vector对象,只需
挑出长度较短的那个,把它的所有元素和另一个vector对象比较即可。例如,如果两
个vector对象的元素分别是0、1、1、2 和 0、1、1、2、3、5、8,则程序的返回
结果为真。*/
vector<int> ivec1{ 1,2,1,4 }, ivec2{ 1,2,1,2,1,4,4 };
size_t j;//用来计数
for (size_t i = 0; i < ivec2.size(); ++i)
{
j = 0;
size_t t = i;//用来到临时递增地址
while (j<ivec1.size()&&ivec1[j]==ivec2[t])
{
j++;//当相同又小于ivec1.size()时加加
t++;//临时下标地址递增
}
if (j==ivec1.size())//当j自增到长度相同表示,前面一样
break;
}
cout<<(bool)j<<endl;//输出一个转换成bool类型的,不是0就是1
system("pause");
return 0;
}
=====================================================================
第5章 语句 165页 范围for语句
=====================================================================
//QQ108201645编写
#include <iostream>
#include <vector>
#include <string>
#include <cctype>
#include <cstddef>
using namespace std;//使用命名空间
#ifndef DECLARATION
#define DECLARATION
//说明部分
//执行已定义声明的话将不再执行
#endif
int main()
{
//vector<int> v{0,1,2,3,4,5,6,7,8,9};
//范围变量必须是引用类型,这样才能对元素执行写操作
//for(auto &r:v)
vector<int> v(10, 2);
for (auto beg = v.begin(), end = v.end(); beg != end; ++beg)
{//在范围for循环中预存了end(),一旦在序列中添加或删除, end函数的值就可以变的无效
auto &r = *beg;
r *= 2;//将v 中的每个值翻倍
}
//do while语句
#ifndef DECLARATION
do
statement
while (condition);
在do语句中, 求condition的值之前首先执行一次statement, condition不能为空.
如果condition的值为空, 循环终止; 否则重复循环过程.condition使用的变量定义
在循环体之外;
#endif
//不断可以使用do while循环(不断地)执行加法运算
string rsp;//作为循环的条件,不能定义在do内部
do
{
cout << "please enter tow values: ";
int val1 = 0, val2 = 0;
cin >> val1 >> val2;
cout << "The sum of " << val1 << " and " << val2
<< " = " << val1 << +val2 << "\n\n"
<< " more ? Enter yes or no:";
cin >> rsp;
} while (!rsp.empty()&&rsp[0]!='n');
//当是空时返回1不是空返回0,用逻辑求反,让它在0时返回1结果为真,右侧则rsp首位不是n则继续循环,否则退出循环
//定义变量
/*
do
{//
numble(foo);
} while (int foo=get_foo);//错误,将变量声明在了do的条件部分
如果允许在条件部分定义变量,则变量的使用出现在定义之前这显然不合理*/
#ifndef DECLARATION
练习5.18说明下列循环的含义并改正其中的错误。
(a) do
int v1, v2;
cout << "Please enter two numbers to sum:";
if (cin >> v1 >> v2)
cout << "Sum is: " << v1 + v2 << endl;
while (cin);
(b) do {
// . . .
} while (ival = get_response());
(c) do {
ival = get_response();
} while (ival);
(a)语法错误,do后面,while前面的语句应该用花括号括起来
(b) 语法错误,do while条件使用的表达式规定必须是在循环前定义!
(c) 语法错误,do while条件使用的表达式规定必须是在循环前定义!;
#endif
string buf, str;
cout<<"请输入任意两组字符串"<<endl;
cin >> buf >> str;
if (buf.size()>str.size())
cout<<buf<<" "<<buf.size()<<endl;
else if (buf.size()==str.size())
cout<<"两个一样长"<<endl;
else
cout<<str<<" "<<str.size()<<endl;
system("pause");
return 0;
}
=====================================================================
第5章 语句 171页 跳转语句
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
#include <cstddef>
using namespace std;//使用命名空间
#ifndef DECLARATION
#define DECLARATION
//说明部分
//执行已定义声明的话将不再执行
#endif
int main()
{
string buf;
while (cin>>buf&&!buf.empty())
{
switch (buf[0])
{
case '-':
//处理第一个空白为止
for (auto it = buf.begin() + 1; it != buf.end(); ++it)
{
if (*it == ' ')
break;
//
}
//bureak #1将控制权转移到这里
break;//#2将离开switch
case '+':
//
break;
}//结束switch
//结束switch:break #2将控制权转移到这里
}//结束while
system("pause");
return 0;
}
=====================================================================
#include <iostream>
#include <string>
#include <cstddef>
using namespace std;//使用命名空间
#ifndef DECLARATION
#define DECLARATION
//说明部分
//执行已定义声明的话将不再执行
#endif
int main()
{/*
练习5.20:编写一段程序,从标准输入中读取string对象的序列直到连续出现两个相
同的单词或者所有单词都读完为止。使用while循环一次读取一个单词,当一个单词
连续出现两次时使用break语句终止循环。输出连续重复出现的单词,或者输出一个
消息说明没有任何单词是连续重复出现的。*/
string newstr, newbuf;
if (cin >> newbuf)
{
int cnt = 1;
while (cin >> newstr)
{
if (newstr == newbuf)
cnt++;
else
{
cout << newbuf << " " << cnt << endl;
newbuf = newstr;
cnt = 1;
}
cout << newbuf << " " << cnt << endl;
}
}
system("pause");
return 0;
}
=====================================================================
第5章 语句 171页 continue语句
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
#include <cctype>
using namespace std;//使用命名空间
#ifndef DECLARATION
#define DECLARATION
//说明部分
//执行已定义声明的话将不再执行
#endif
int main()
{
string buf;
while (cin>>buf&& !buf.empty())
{
if (buf[0]!=' ')
continue;//接着读取下一个输入
//程序执行过程到了这里?说明当前的输入是以下画线开始的;接着处理buf......
}
/*练习5.21修改5.5.1节练习题的程序,使其找到的重复单词必须以大写字母开头。*/
string newstr, newbuf;
if (cin >> newbuf)
{
while (cin >> newstr)
{
if (newstr == newbuf&&isupper(newbuf[0]))
{
cout << newbuf << endl;
}
else
{
newbuf = newstr;
}
}
}
system("pause");
return 0;
}
=====================================================================
第5章 语句 172页 goto语句
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
#include <cctype>
using namespace std;//使用命名空间
#ifndef DECLARATION
#define DECLARATION
//说明部分
//执行已定义声明的话将不再执行
#endif
int main()
{
//goto作用是无条件跳转到同一函数内的另一条语句,不要在程序中使用,会使得程序既难理解又难修改
//goto label
int ix = 0;
goto end;//跳转到标签为end的代码位置
ix=5;//如果把上面int ix放在这里,程序将出错.会绕过一个带初始化的变量定义
cout<<ix<<endl;
end:
ix = 42;
cout << ix << endl;
//向后跳过一个已经执行定义的是合法的
int get_size();
#ifndef DECLARATION
begin:
int sz = get_size();
if (sz <= 0)
{
goto begin;//跳回begin后会一直循环
}
//上面代码goto执行后将销毁sz,因为跳回begin的动作跨过了sz的定义语句,所以sz将重新定义并初始化
#endif
for (int sz = get_size(); sz <= 0; sz = get_size())
;//与上述代码功能一样的重写//作业5.22
system("pause");
return 0;
}
int get_size()
{
return 0;
}
=====================================================================
第5章 语句 173页 try语句块和异常处理
=====================================================================
//QQ108201645编写
//定义为Sales_item.h头文件
#ifndef SALESITEM_H//未定义SALESITEM_H则继续向下执义
// we're here only if SALESITEM_H has not yet been defined
#define SALESITEM_H
#include "Version_test.h"
// Definition of Sales_item class and related functions goes here
#include <iostream>
#include <string>
class Sales_item {
// these declarations are explained section 7.2.1, p. 270
// and in chapter 14, pages 557, 558, 561
friend std::istream& operator>>(std::istream&, Sales_item&);
friend std::ostream& operator<<(std::ostream&, const Sales_item&);
friend bool operator<(const Sales_item&, const Sales_item&);
friend bool
operator==(const Sales_item&, const Sales_item&);
public:
// constructors are explained in section 7.1.4, pages 262 - 265
// default constructor needed to initialize members of built-in type
#if defined(IN_CLASS_INITS) && defined(DEFAULT_FCNS)
Sales_item() = default;
#else
Sales_item() : units_sold(0), revenue(0.0) { }
#endif
Sales_item(const std::string &book) :
bookNo(book), units_sold(0), revenue(0.0) { }
Sales_item(std::istream &is) { is >> *this; }
public:
// operations on Sales_item objects
// member binary operator: left-hand operand bound to implicit this pointer
Sales_item& operator+=(const Sales_item&);
// operations on Sales_item objects
std::string isbn() const { return bookNo; }
double avg_price() const;
// private members as before
private:
std::string bookNo; // implicitly initialized to the empty string
#ifdef IN_CLASS_INITS
unsigned units_sold = 0; // explicitly initialized
double revenue = 0.0;
#else
unsigned units_sold;
double revenue;
#endif
};
// used in chapter 10
inline
bool compareIsbn(const Sales_item &lhs, const Sales_item &rhs)
{
return lhs.isbn() == rhs.isbn();
}
// nonmember binary operator: must declare a parameter for each operand
Sales_item operator+(const Sales_item&, const Sales_item&);
inline bool
operator==(const Sales_item &lhs, const Sales_item &rhs)//==运算符重载
{
// must be made a friend of Sales_item
return lhs.units_sold == rhs.units_sold &&
lhs.revenue == rhs.revenue &&
lhs.isbn() == rhs.isbn();
}
inline bool
operator!=(const Sales_item &lhs, const Sales_item &rhs)
{
return !(lhs == rhs); // != defined in terms of operator==
}
// assumes that both objects refer to the same ISBN
Sales_item& Sales_item::operator+=(const Sales_item& rhs)//'+='重载
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
// assumes that both objects refer to the same ISBN
Sales_item
operator+(const Sales_item& lhs, const Sales_item& rhs)//'+'重载
{
Sales_item ret(lhs); // copy (|lhs|) into a local object that we'll return
ret += rhs; // add in the contents of (|rhs|) 调用+=重载
return ret; // return (|ret|) by value 返回临时对象
}
std::istream&
operator>>(std::istream& in, Sales_item& s)//重载输入运算符
{
double price;
in >> s.bookNo >> s.units_sold >> price;//接受书的编号、数量、价格
// check that the inputs succeeded
if (in)//输入不等于空、比如输入ctrl+z再回车
s.revenue = s.units_sold * price;//收入等于数量乘价格
else
s = Sales_item(); // input failed: reset object to default state
//调用构造函数重置为默认。
return in;
}
std::ostream&
operator<<(std::ostream& out, const Sales_item& s)
{
out << s.isbn() << " " << s.units_sold << " "
<< s.revenue << " " << s.avg_price();
return out;
}
double Sales_item::avg_price() const
{
if (units_sold)//除数不等于0
return revenue / units_sold;
else
return 0;
}
#endif
―――――――――――――――――――――――――――――――――――――――
//续上面部分保存任意main.cpp
#include <iostream>
#include <string>
#include <cctype>
#include "Sales_item.h"
using namespace std;//使用命名空间
#ifndef DECLARATION
#define DECLARATION
//说明部分
//执行已定义声明的话将不再执行
#endif
int main()
{
#ifndef DECLARATION
当程序的某部分检测到一个它无法处理的问题时, 需要用到异常处理;
throw表达式(throw expression), 异常检测部分使用throw表达式来表示它遇到了无法处理的问题.
我们说throw引发(raise)了异常;
try语句块(try block), 异常处理部分使用try语句块处理异常.try语句块以关键字try抛出的异常
通常会被某个catch子句(catch clause)结束.畎因为catch子句"处理"异常, 所以它们也被称作
异常处理代码(exception handler);
一套异常类(exception class), 用于在throw表达式和相关 catch子句之间传递异常的具体信息;
#endif //DECLARATION
//以下代码需要Sales_iter.h头文件,参见第一章
#ifndef DECLARATION
Sales_item item1, item2;
cin >> item1 >> item2;
//首先检查item1和item2是否表达同一本书
if (item1.isbn() == item2.isbn())
{//当程序执行到这里表示相同
cout << item1 + item2 << endl;
}
else
{
cerr<<"Data must refer to same ISBN "<<endl;
}
#endif
/*
try语句块的通用语法形式是
try
{
program-statements
}
catch (exception-declaration)//译:接住(异常-声明)
{
program - statements(译:编程-说明)
}
catch (exception-declaration)
{
program-statements
}
catch (exception-declaration)
{
program-statements
}
*/
//修改上面这段Sales_item
Sales_item item1, item2;
while (cin>>item1>>item2)
{
try
{
if (item1.isbn() != item2.isbn())//娄不一样时抛出一个异常.跳转到catch去处理异常
throw runtime_error("Data must refer to same ISBN");
//throw必须在 try代码块中.后边跟的值决定抛出异常的类型。
cout<<item1+item2<<endl;
}
catch (runtime_error err)
{
//catch
//出现在try代码块后,后边跟的数据决定捕获的类型
//catch (...) 表示捕获所有异常
cout<<err.what()//err.what是runtime_error类的一个成员函数
<<"\nTry Again? Enter y or n"<<endl;
char c;
cin >> c;
if (!cin||c=='n')
break;//跳出while循环
}
}
system("pause");
return 0;
}
#ifndef DECLARATION
exception头文件定义了最通用的异常类exception.它只报告异常的发生
不提供任何额外信息;
stdexcept头文件定义了几种常用的异类, 详细信息在表5.1中列;
new头文件定义了bad_alloc异常类型, 这种类型将在12.1.2节第407页中详细介绍;
type_info头文件定义了bad_cast异常类型,这种在19.2第731页详细介绍
#endif
=====================================================================
//QQ108201645编写
#ifndef DECLARATION
练习5.24:修改你的程序,使得当第二个数是0时抛出异常。先不要设定catch子句,运行程序并真的为除数输入0,看看会发生什么?
程序终止。
练习5.25:修改上一题的程序,使用try语句块去捕获异常。catch子句应该为用户输出一条提示信息,询问其是否输入新数并重新执行try语句块的内容。
#endif
#include <iostream>
#include <string>
#include <map>
#include "Sales_item.h"
using namespace std;//使用命名空间
#ifndef DECLARATION
#define DECLARATION
//说明部分
//执行已定义声明的话将不再执行
#endif
int main()
{
int i1, i2;
while (cin >> i1 >> i2)
{
try
{
if (i2 == 0)
throw string("ERROR! Division by zero!");
}
catch (string s)
{
cout << s << endl;
cout << "Try again? 'Y' or 'N'";
char c;
cin >> c;
if (!cin || c == 'n')
break;
else
continue;
}
cout << i1 / i2 << endl;
}
system("pause");
return 0;
}
=====================================================================
表5.1: <stdexcept>定义的异常类 | |
exception | 最常见的问题 |
runtime_error | 只有在运行时才能检查出的问题 |
range_error | 运行时错误:生成的结果超出了有意义的域值范围 |
overflow_error | 运行时错误:计算上溢 |
underflow_error | 运行时错误:计算下溢 |
logic_error | 程序逻辑错误 |
domain_error | 逻辑错误:参数对应的结果值不存在 |
invalid_error | 逻辑错误:无效参数 |
length_error | 逻辑错误:试图创建一个超出该类型最大长度的对象 |
out_of_range | 逻辑错误:使用一个超出有效范围的值 |