写在前面:作为一只小白,感谢小甲鱼老师提供这么好的入门课程。因此在这里做个笔记,如有侵权请联系删除
www.fishc.com
①assert函数
1.1、C语言和C++ 都有一个专为调试而准备地工具函数,就是assert()函数
1.2, 这个函数是在C语言的assert.h库文件里定义的,所以包含到C++教程里我们用以下语句:
---#include <assert.h>
assert()函数需要有一个参数,它将测试这个输入参数的真or假状态
如果为真,Do nothing!
如果为假,Do something!
1.3,看下面演示:
#include <cassert>
int main()
{
int i = 20;
assert(i == 65);
return 0;
}
演示中,我们看到assert()函数可以帮助我们调试程序。
我们可以利用它在某程序里的关键假设不成立时立刻停止该程序的执行并报错,从而避免发生更严重的问题
另外,除了结合assert()函数,在程序的开发,测试阶段,我们可以使用大量的cout语句来报告在程序里正在发生的事情。
②用户体验和程序猿体验的角度
》》对运行时错误的处理分为两种情况:
一种与程序猿有关,在开发,测试和调试程序的过程中,程序猿需要尽可能详细的信息来查找和纠正各种潜在的运行时错误。
另一种情况与最终用户有关,在使用一个程序的过程中,错误处理应该把用户的感受摆在第一位!
》》在理想情况下,程序发布之前,它里边的所有错误都应该被发现和改正过来。只可惜这是几乎不可能的,就连微软和google这样的大公司也做不出这样的保证!
注意,我们这几节课讨论的内容是从程序猿的角度展开的。这里介绍的一些技巧不应该用在一个即将交付给最终用户的应用程序里。因为这样的用户使用体验会插到极点。
最后一条原则:最终用户看到的错误信息应该既专业又清晰,不能轻易中断程序,不能充满技术细节。
③捕获异常
同样为了对付潜在的编程错误(尤其是运行时的错误),捕获异常是一种完全不同的办法。
简单地说,异常(exception)就是与预期不相符合的反常现象。
基本使用思路:
1.安排一些C++代码(try语句)去尝试某件事-----尤其是那些可能会失败的事(比如打开一个文件或申请一些内存)
2.如果发生问题,就抛出一个异常(throw语句)
3.再安排一些代码(catch语句)去捕获这个异常并进行相应的处理。
捕获异常的基本语法
try
{
//Do something
//Throw an exception on error
}
catch
{
//Do whatever
}
Pay attention!每条try语句至少要有一条配对的catch语句。必须定义catch语句以便让它接收一个特定类型的参数。
C++还允许我们定义多条catch语句,让每条catch语句分别对应着一种可能的异常类型:
---catch (int e) {...}
---catch (bool e){...}
---catch (...){....}
最后一条catch语句可以捕获任何类型的异常。
在程序里,我们可以用throw保留字来抛出来一个异常:throw 1;
在某个try语句块里执行过throw语句,它后面的所有语句(截至到这个try语句块末尾)将永远不会被执行。
与使用一个条件语句或return语句相比,采用异常处理机制的好处是它可以把程序的正常功能和逻辑出错处理部分清晰的划分开而不是让它们混杂在一起。
④如何让函数抛出异常
你可以在定义一个函数时明确地表明你想让它抛出一个异常,为了表明你想让它抛出那种类型地异常,可以使用如下所示语法:
---type functionName(arguments)throw(type);
如果没有使用这种语法来定义函数,这意味着函数可以抛出任意类型地异常。
注:有些编译器不支持这种语法,则可省略throw(type)部分。
#if 1
#include <iostream>
#include <climits>
unsigned long returnFactorial(unsigned short num) throw(const char *);
int main()
{
unsigned short num = 0;
std::cout << "please input int num: ";
while(!(std::cin >> num)||(num < 1))
{
std::cin.clear();//清除状态
std::cin.ignore(100,'\n');//清除缓冲区
std::cout << "please input int num: ";
}
std::cin.ignore(100,'\n');
try
{
unsigned long factorial = returnFactorial(num);
std::cout << num << "Factorial result: " << factorial << "\n";
}
catch(const char *e)
{
std::cout << e;
}
return 0;
}
unsigned long returnFactorial(unsigned short num) throw(const char *)
{
unsigned long sum = 1;
unsigned long max = ULONG_MAX;
for(int i = 1; i <= num; i ++)
{
sum += i;
max /= i;
}
if(max < 1)
{
throw "Factorial failed over max\n";
}
else
{
return sum;
}
}
#else
#include <iostream>
#include <climits>
using namespace std;
unsigned long returnFactorial(unsigned short num) throw (const char *);
int main()
{
unsigned short num = 0;
cout << "请输入一个整数: ";
while( !(cin>>num) || (num < 1))
{
cin.clear(); //清除状态
cin.ignore(100, '\n'); //清除缓冲区
cout << "请输入一个整数: ";
}
cin.ignore(100, '\n');
try
{
unsigned long factorial = returnFactorial(num);
cout << num << "的阶乘值是: " << factorial << endl;
}
catch(const char *e)
{
cout << e;
}
return 0;
}
unsigned long returnFactorial(unsigned short num) throw (const char *)
{
unsigned long sum = 1;
unsigned long max = ULONG_MAX;
for(int i=1; i<=num; i++)
{
sum *= i;
max /=i;
}
if (max < 1)
{
throw "悲催...该基数太大,无法在该计算机计算求出阶乘值.\n";
}
else
{
return sum;
}
}
#endif
但是编译报警告了
factorial_exception.cpp:5:51: warning: dynamic exception specifications are deprecated in C++11 [-Wdeprecated]
5 | unsigned long returnFactorial(unsigned short num) throw(const char *);
| ^~~~~
factorial_exception.cpp:35:51: warning: dynamic exception specifications are deprecated in C++11 [-Wdeprecated]
35 | unsigned long returnFactorial(unsigned short num) throw(const char *)
解释如下https://discourse.itk.org/t/warning-dynamic-exception-specifications-are-deprecated-in-c-11/470/13
只是编译警告,意思是C ++11 已经抛弃这种用法,但是仍然可以使用,这是C++地一种编译警告方法
⑥TIPS
如何使用异常是一个很容易引起争论地话题。有些程序猿使用异常来处理几乎所有地错误,但C++创始人Bjarne Stroustrup觉得它们正在被滥用。
所以使用异常地基本原则是:应该只用它们来处理确实可能不正常地情况。
做为一条原则:在构造器和析构器里面不应该使用异常。一位非常有经验地程序猿在这些方法里成功地使用了异常是有可能地,但稍有不慎就会导致严重地问题。
如果try语句块无法找到一个与之匹配地catch语句块,它抛出地异常将中止程序地执行。
在C++标准库里有一个名为exception地文件,该文件声明了一个exception地基类。可以用这个基类来创建个人地子类以管理异常。
有经验地程序猿常常这么做,而如此抛出和捕获地是exception类或其子类地对象。
如果你打算使用对象作为异常,请记住这样一个原则:以“值传递”方式抛出对象,以“引用传递”方式捕获对象。