程序员需要理解错误捕获以及处理错误情况
3.3.1 错误类型
两种基本错误情况:
- 用户错误:用户做了不正确的事情引发的错误
- 程序员错误:代码本身的BUG所引起的错误,若程序员不犯错问题可以避免
3.3.2 错误处理
在理想情况下所有的程序员错误都应该在产品发布前被修复,处理程序员错误的方法是中止并通知,处理用户错误的方法是通知并且继续。但是在实际情况中往往根据实际情况灵活决定处理方式以提高效率。
3.3.3 实现错误检测及处理
3.3.3.1 错误返回码
设计枚举类型函数,向上层返回一定值来表示错误类型
3.3.3.2 异常
错误返回码的问题是检测到错误的函数与可处理错误的函数完全无关。如果在堆栈顶部的函数遇到错误其下面的全部函数都需要逐一传送代码造成冗杂。
抛出异常 检测到错误的函数的情况下就可以把错误信息传给其他的代码,程序进行堆栈辗转开解,调用相应的try函数与catch中的代码逐个比对,找到匹配的就会执行catch的语句。在辗转开解寻找try的过程中会自动调用所有变量的析构函数。
析构函数:当一个类的对象离开作用域时,析构函数将被调用(系统自动调用)。析构函数的名字和类名一样,不过要在前面加上 ~ 。对一个类来说,只能允许一个析构函数,析构函数不能有参数,并且也没有返回值。析构函数的作用是完成一个清理工作,如释放从堆中分配的内存。
一个类中可以有多个构造函数,但析构函数只能有一个。对象被析构的顺序,与其建立时的顺序相反,即后构造的对象先析构。
运行负担:使用异常会造成调用帧变大因为要存储多出的信息。并且堆栈的辗转开解很慢。因此由此引擎中通常关闭异常。
3.3.3.3 断言
断言是一行检查表达式的代码表达式求值表达式求值为真则继续为假则直接中止程序打印消息。
断言通过#define来实现,游戏发行时可以去除断言来提高运行效能
断言实现 定义在宏中的一个if/else判断
#if ASSERTIONS_ENABLED
#define debugBreak() asm(int 3;)
#define ASSERT(expr)
if(expr){}
else
{
reportAssertionFailure(#expr,__FILE__,__LINE__);
debugBreak();
}
#else
#define ASSERT(expr)
#endif
#define ASSERT(expr) if(!(expr)) debugBreak();
通常的ASSERT宏可以在所有生成中保留,第二种SLOW_ASSERT只在调试生成中有效
断言应该用来捕捉严重错误。