目录
练习进度
5.22
for(int sz=get_size();sz<=0;sz=get_size());
5.24
int main() {
int m, n;
cout << "输入被除数m和除数n:";
cin >> m >> n;
if (n == 0)
throw runtime_error("除数不能为0!");
int out = m / n;
cout << "计算结果为:" << out << endl;
}
/*报未处理的异常:
0x76764192 处(位于 C++Primer.exe 中)有未经处理的异常: Microsoft C++ 异常: std::runtime_error,位于内存位置 0x00AFFD6C 处。*/
5.25
int main() {
int m, n;
cout << "输入被除数m和除数n:";
while (cin >> m >> n)
{
try {
if (n == 0)
throw runtime_error("除数不能为0!");
}
catch(runtime_error err){
cout << err.what();
cout << "\n是否重新输入?y/n" << endl;
char c;
cin >> c;
if (!cin||c == 'n')
break;
}
}
int out = m / n;
cout << "计算结果为:" << out << endl;
}
6.10
void exchange(int *p1, int *p2){
int tmp=*p1;
*p1=*p2;
*p2=tmp;
}
6.33
void readVec(vector<int> arr,int flag) {
if (flag < arr.size())
{
cout << arr[flag] << " ";
readVec(arr, flag + 1);
}
}
int main() {
vector<int> vec = { 1,2,3,4,5,6,7 };
readVec(vec, 0);
}
知识点记录
不要在程序中使用goto
语句,因为它使得程序既难理解又难修改。
5.6 try和异常(exception)处理
异常处理机制为程序中异常检测和异常处理这两部分的协作提供支持。
- 异常处理机制包括:
throw表达式:异常检测部分使用throw
表达式来表示它遇到了无法处理的问题。我们说throw
引发(raise)了异常。
try语句块:try
语句块以关键字try
开始, 并以一个或多个catch
子句结束。try
语句块中代码抛出的异常通常会被某个catch
子句处理。因为catch
子句“处理”异常,所以它们也被称作异常处理代码。
一套异常类:用于在throw
表达式和相关的catch
子句之间传递异常的具体信息。
抛出异常将终止当前的函数,并把控制权转移给能处理该异常的代码。
- 常见异常类
exception头文件定义了exception异常类,只报告一场发生,不提供额外信息。
stdexcept头文件定义如下异常类:
new头文件定义了bad_alloc异常类型。
type_info头文件定义了bad_cast异常类型。
注意:
我们只能以默认初始化的方式初始化exception、bad_alloc和bad_cast对象,不允许为这些对象提供初始值。
其他异常类型的行为则恰好相反应该使用string对象或者C风格字符串初始化这些类型的对象,但是不允许使用默认初始化的方式。当创建此类对象时,必须提供初始值,该初始值含有错误相关的信息。
6.1 函数基础
局部静态变量在函数中只会定义一次,以后再次执行时会自动忽略该句定义。
6.2 参数传递
引用传参可以避免拷贝,如果函数无须改变引用形参的值,最好将其声明为常量引用。例如:
bool isShorter(const string& s1, const string& s2) {
return s1.size() < s2.size();
}
C++允许我们定义若干具有相同名字的函数,不过前提是不同函数的形参列表应该有明显的区别。
不能用字面值作为非常量引用的实参。
- 二维数组的形参
//matrix指向数组的首元素,该数组的元素是由10个整数构成的数组
void exam(int (*matrix)[10], int rowsize){/*...*/}
6.3 返回类型
返回引用类型
- 当函数返回引用类型时,没有复制返回值,相反,返回的是对象本身。
- 在函数的参数中,包含有以引用方式或指针方式存在的,需要被返回的参数。
- 不能返回局部对象的引用或指针!
返回数组指针
数组无法被拷贝,所以函数不能返回数组。
但是函数可以返回数组的指针或引用,有两种办法:
- 使用类型别名
typedef int arrT[10]; //arrT是一个类型别名,表示含有10个整数的数组
using arrT=int[10]; //arrT的等价声明
arrT* func(int i); //func返回一个指向含有10个整数的数组的指针
- 声明一个返回数组指针的函数
int (*func(int i))[10];
逐层理解该声明的含义:
func (int i)
表示调用func函数时需要一个int类型的实参。
(*func (int i))
意味着我们可以对函数调用的结果执行解引用操作。
(*func (int i)) [10]
表示解引用func的调用将得到一个大小是10的数组。
int (*func (int i)) [10]
表示数组中的元素是int类型。
- 尾置返回类型
//不太明白
//func 接受一个int 类型的实参,返回一个指针,
//该指针指向含有10 个整数的数组
auto func(int i) - > int(*) [10];
6.4 函数重载
如果同一作用域内的几个函数名字相同但形参列表不同,我们称之为重载(overloaded) 函数。
- main函数不能重载
6.5 特殊用途语言特性
- 函数有多个默认实参是调用时可以省略形参输入(只能从前往后省略)。
- 一旦函数的某个形参被赋予了默认值,他后面所有的参数都必须有默认值
内联函数
一次函数调用其实包含着一系列工作:调用前要先保存寄存器,并在返回时恢复可能需要拷贝实参;程序转向一个新的位置继续执行。
- 内联函数可以避免函数调用开销。
将函数指定为内联函数(inline),通常就是将它在每个调用点上“内联地”展开。
constexpr 函数(constexpr function ) 是指能用于常量表达式的函数。定义constexpr 函数的方法与其他函数类似,不过要遴循几项约定:函数的返回
类型及所有形参的类型都得是字面值类型。
assert和NDEBUG
assert是一种预处理宏,表达式:assert(expr);
expr为0:输出信息并中止程序执行;
expr为1:assert什么都不做。
assert常用来检查不可能发生的事情。
NDEBUG没有很好的理解,只知道:
assert依赖于一个NDEBUG的预处理变量的状态,如果定义了NDEBUG,assert什么也不做,默认状态下NDEBUG是未定义的。编译器也可以预先定义该变量。