【C++】《给游戏开发者的C++》笔记 第五章 异常管理

C++引入了新的报错处理机制:异常管理(exception handling),它允许我们的代码健壮而简约、能够处理任何意料之外情况的报错。此章将覆盖使用异常的原因、怎么有效的使用异常、和使用异常的弊端,尤其是它们各自对性能的影响。结尾时,我们会对游戏编程中的异常提出一些建议。

5.1管理报错

从计算机的发明开始,程序员就不得不处理报错,从没有过理想的解决方案:不是白花经理和时间来杞人忧天、就是干脆假装看不见。

1.无视它们!

虽然听起来荒谬,但这就是大多数程序的错误处理方法(对大多数错误而言)。比方说,我们当然要检出某个文件是否正常开启,但谁在乎printf有没有成功呢?如果一切正常,倒还好说,但要是一旦出错,程序就有可能崩溃。要是没有足够的内存了呢?要是桌面显示设置的是8比特色彩深度呢?如果没有足够的磁盘空间了呢?或者用户在游戏加载时候把CD-ROM拔出来了呢?在快速且混乱的工具——甚至是内部开发工具中,忽略它们是可行的(取决于公司内部的质量保准预期)。但这显然不是发售版本程序应有的方案,所以我们要找替代方案。

2.使用错误代码(error code)

我们可以用被时间检验过的经典方法:在函数返回值报错,任何可能失败的函数都会返回错误码,或者至少一个布尔值,表明函数是否成功。调用代码负责检查返回值以及正确地处理错误。

理论上,这个方法没问题;实际上,它难以在项目整体中运用。原因有三:首先,代码可读性往往因此降低,原来两句话的函数可能变成了长达三十行的、充满了if-then-else判断句的垃圾,不仅丑陋而且混淆了函数真正的目的。以下代码从数据流中加载了一个静态网格体,第一个函数没做报错检查,而第二个做了,哪个更可读呢?

void Mesh::Load(Stream stream)
{
    ParseHeader (stream);
    ParseFlags(stream); 
    ParseVertices(stream);
    ParseFaces(stream);
}
int Mesh::Load(Stream stream)
{
    int errCode = OK;
    errCode = ParseHeader(stream);
    if (errCode != OK)
    {
        FreeHeader();
        return errCode;
    }
    errCode = ParseFlags(stream);
    if (errCode != OK)
    {
        FreeHeader();
        return errCode;
    }
    errCode = ParseVertices(stream);
    if (errCode != OK)
    {
        FreeHeader();
        FreeVertices();
        return errCode;
    }
    errCode = ParseFaces(stream);
    if (errCode != OK)
    {
        FreeHeader();
        FreeVertces();
        FreeFaces();
        return errCode;
    }
    return errCode; 
}

源代码自己会说话。其次,检查错误代码不仅丑陋麻烦,也很浪费:要是每个函数调用都被一群判断语句包围,那游戏的性能就会大幅下降。

最后是程序装配的问题:错误代码应当在哪里?是一个所有类都会包含的大文档?还是每个子系统都有各自的一套?它们如何从一串数字编程人类可读的字符串?一定有更好的方案。

3. 扔炸药包(blow up)

另一种方法是双手投降,但用插入(assert)方法来阻止程序崩溃。只要停下了程序,就能报告更多的信息,比如文件名和错误的行号、描述性信息,总比没有好。

4. 使用setjmp()和longjmp()

硬核C程序员可能或熟悉这两个函数,setjmp()允许我们在代码(以及堆栈状态)里预留调用位置,而longjmp()将程序恢复到指定的位置和栈状态。按理说,每当有某种错误时,我们能调用longjmp()来处理,不幸的是,这个方法虽然灵活但和C++不太合拍:它虽然释放了栈,但没有销毁栈中的对象,于是对象的析构函数永远不会被调用,这意味着潜在的内存泄漏或者未释放的资源。如果在出错后只需退出程序,那么问题不大,然而如果需要从报错处恢复并继续运行,此方法会导致内存耗尽从而程序崩溃。

5.使用C++异常

终于到了重点了。目前为止的报错处理机制都有各式的缺点。在看异常的语法之前,不如先看它的常见用法和它相比前几个机制的优点何在。

1.它的原理

当程序碰到预料之外的任何事,都可以扔个异常,使程序跳转到最近的异常处理块。如果整个函数里都没有异常处理块,就释放栈(正确的销毁栈中的每个对象)然后去上个层级寻找,重复这个过程直到找到异常处理块;又或者到了顶部也没找到——这时会触发默认异常处理代码,程序停止。

在异常处理块中,我们想干啥都行:报错、尝试解决、甚至无视它。也可以对症下药,取决于异常的原因,(比如除零和文件损坏这两种问题应当有不同的处理方法),在异常处理块运行之后,程序直接继续运行,而不是回到异常抛出的位置。

2.优势

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值