荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: Second (石开), 信区: Program
标  题: Part of C++ FAQ Lite[17]--异常和错误处理
发信站: 荔园晨风BBS站 (Thu Jun  7 05:51:36 2001), 转信

译文来自:http://www.sunistudio.com/sunidoc.asp  by nicrosoft
[17] 异常和错误处理
(part of c++ faq lite, copyright ? 1991-96, marshall cline, cline@parashift.
com)
----------------------------------------------------------------------------
----
faqs in section [17]:
[17.1] 如何处理构造函数的失败?
[17.2] 如果构造函数可能抛出异常,我该怎样处理资源?
[17.3] 当别人抛出异常时,我如何改变字符数组的字符串长度来防止内存泄漏?
----------------------------------------------------------------------------
----
[17.1] 如何处理构造函数的失败?
抛出一个异常。
构造函数没有返回类行,所以返回错误代码是不可能的。因此抛出异常是标记构造
函数失败的最好方法。
如果你没有或者不愿意使用异常,这里有一种方法。如果构造函数失败了,构造函
数可以把对象带入一种“僵尸”状态。你可以通过设置一个内部状态位使对象就象
死了一样,即使从技术上来说,它仍然活着。然后加入一个查询(“检察员”)成
员函数,以便类的用户能够通过检查这个“僵尸位”来确定对象是真的活着还是已
经成为僵尸(也就是一个“活着的死对象”)。你也许想有另一个成员函数来检查
这个僵尸位,并且当对象并不是真正活着的时候,执行一个 no-op(或者是更令人
讨厌的如 abort())。这样做真的不漂亮,但是如果你不能(或者不想)使用异常
的话,这是最好的方法了。
----------------------------------------------------------------------------
----
[17.2] 如果构造函数可能抛出异常,我该怎样处理资源?
你的对象中的每个数据成员应该清理自己。
如果构造函数抛出异常,对象的析构函数将不会运行。如果你的对象需要撤销一些
动作(如分配了内存,打开了一个文件,或者锁定了某个信号量),这些需要被撤
销的动作必须被对象内部的一个数据成员记住。
例如,你应该将分配的内存赋给对象的一个“智能指针”成员对象 fred,而不是
分配内存给未被初始化的 fred* 数据成员。这样当该智能指针(译者注:即 fred
对象)死亡时,智能指针的析构函数将会删除 fred 对象。
标准类 auto_ptr 就是这种“智能指针”类的一个例子。你也可以写你自己的引用
计数智能指针。你也可以用智能指针来指向磁盘记录或者其它机器上的对象。
----------------------------------------------------------------------------
----
[17.3] 当别人抛出异常时,我如何改变字符数组的字符串长度来防止内存泄漏?
如果你要做的确实需要字符串,那么不要使用字符数组,因为数组会带来麻烦。你
应该用一些类似字符串类的对象来代替。
例如,假设你想得到一个字符串的拷贝,随意修改这个拷贝,然后在修改过的拷贝
的字符串末尾添加其它的字符串。字符数组方法将是这样:
    void usercode(const char* s1, const char* s2)
    {
      // 将s1的拷贝放入一个新的名称为 copy 的字符串:
      char* copy = new char[strlen(s1) + 1];
      strcpy(copy, s1);
      // 现在我们有了一个局部指针来访问分配的内存
      // 我们需要用一个 try 块来防止内存泄漏:
      try {
        // 现在我们随意乱动 copy...
        // ...
        // 随后我们将 s2 添加到被修改过的 copy 末尾:
        // ... [在此处重分配 copy] ...
        char* copy2 = new char[strlen(copy) + strlen(s2) + 1];
        strcpy(copy2, copy);
        strcpy(copy2 + strlen(copy), s2);
        delete[] copy;
        copy = copy2;
        // 最后我们再次随意乱动 copy...
        // ...
      } catch (...) {
        delete[] copy;   // 得到一个异常时,防止内存泄漏
        throw;           // 重新抛出当前的异常
      }
      delete[] copy;     // 没有得到异常时,防止内存泄漏
    }
象这样使用 char* s 是单调的并且容易发生错误。为什么不使用一个字符串类的
对象呢?你的编译器也许提供了一个字符串类,而且它可能比你自己写的 char*
更快,当然也更简单、更安全。例如,如果你使用了标准化委员会的字符串类,
你的代码看上去就会象这样:
    #include <string>           // 让编译器找到 string 类
    using namespace std;
    void usercode(const string& s1, const string& s2)
    {
      // 将 s1 的拷贝放入一个新的string对象,名称为 copy
      string copy = s1;         // 注意:我们不需要 try 块
      // 现在我们随意乱动 copy...
      // ...
      // 随后我们将 s2 添加到被修改过的 copy 末尾:
      copy += s2;               // 注意:我们不需要重新分配内存
      // 最后我们再次随意乱动 copy...
      // ...
    }                           // 注意:我们不需要 delete[]

--
                            既然热爱生命
                            那么,
                            一切都在意料之中。

※ 来源:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM: 192.168.28.16]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店