gpt4 book ai didi

c++ - 用于异常处理的基类技术

转载 作者:可可西里 更新时间:2023-11-01 17:59:38 27 4
gpt4 key购买 nike

GotW#8的任务是在 C++ 中实现一个异常中立的通用堆栈数据结构,假设只有模板参数的析构函数不抛出。诀窍是处理可能抛出的模板参数操作(构造函数、复制构造函数、赋值),以便在它们抛出时使堆栈保持一致状态。

在解决方案中,Herb Sutter 说

To keep this solution simpler, I've decided not to demonstrate the base class technique for exception-safe resource ownership.

谷歌搜索后,我找到了 this answer由 Dave Abrahams 于 1997 年提出。在他的解决方案中,他处理基类中内存的分配和删除,并在子类中实现堆栈操作。这样,他确保复制构造函数中的元素复制与内存分配分开,这样如果复制失败,无论如何都会调用基类析构函数。

作为引用,这是 Dave 的复制构造函数,其中添加了我的评论:

// v_ refers to the internal array storing the stack elements
Stack(const Stack& rhs)
: StackBase<T>( rhs.Count() ) // constructor allocates enough space
// destructor calls delete[] appropriately
{
while ( Count() < rhs.Count() )
Push( rhs.v_[ Count() ] ); // may throw
}

如果基构造函数成功,即使子类的复制构造函数抛出,也能保证基析构函数中的内存清理。

我的问题是:

  1. 除上文所述外,该方法还有其他好处吗?
  2. 我自己解决这个问题时想出了这个复制构造函数:

    // v_ refers to the internal array storing the stack elements
    // vsize_ is the amount of space allocated in v_
    // vused_ is the amount of space used so far in v_
    Stack (const Stack &rhs) :
    vsize_ (0), vused_ (0), v_ (0) {
    Stack temp (rhs.vused_); // constructor calls `new T[num_elements]`
    // destructor calls `delete[] v_`
    std::copy (rhs.v_, rhs.v_ + rhs.vused_, temp.v_); // may throw
    swap (temp);
    }
    void swap (Stack &rhs) {
    std::swap (v_, rhs.v_);
    std::swap (vused_, rhs.vused_);
    std::swap (vsize_, rhs.vsize_);
    }

    与这种方法相比,我发现使用基类有点麻烦。有什么理由应该优先使用基类技术而不是这种临时复制然后交换的方法吗?请注意,Dave 和我都已经有了 swap() 成员,因为我们在 operator=() 中使用了它。

  3. Dave Abrahams 的技术似乎不是很出名(根据 Google 的说法)。它有不同的名称吗?这是标准做法吗?我错过了什么吗?

注意事项:

  • 假设循环中 Dave 的 Push() 等同于我对 std::copy 的使用
  • 让我们将智能指针排除在答案之外,因为它们的使用会带走本练习中显式管理内存的重点

最佳答案

从行为上来说,这两个实现是相同的。他们都设置了一个托管内存分配对象,如果构造函数失败,该对象将在作用域退出时进行清理。复制到临时变量可能会更昂贵,但如评论中所述,std::move可能会抵消这些额外费用。在回答您的具体问题时:

  1. Abraham 的示例确实将堆分配与您的实际类实现细节相分离。在您的代码中,如果您在复制数组之前/之后执行更复杂的内存操作,则确保正确管理所有实体可能会稍微困难一些。否则,关于第一个实现的行为,我看不到任何您尚未涵盖的明确细节。
  2. Abraham 的实现确实将清理抽象到一个位置。如果多个类使用 StackBase<T>然后他们每个人都可以安全地假设如果他们抛出异常,他们的动态内存将被清理。在您的实现中,您需要重写(至少部分)临时对象和交换代码以实现相同的目的。实际上,此实现减少了实现 StackBase 的多个子类的行数。但是,如果您希望在其他一些基类之上分配额外的内存,则您的实现会避免多重继承。您的代码还避免了模板代码膨胀编译时间/大小——尽管无论如何我通常不认为这在大多数情况下是一个很大的负面影响。除非我尝试编写非常通用的用例代码,否则我可能会默认使用与您的代码接近的内容。
  3. 我不知道这种方法是否有特定的名称——如果我找到一个,我会更新它——但我之前至少在一本 C++ 编程书籍中看到过它。

关于c++ - 用于异常处理的基类技术,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15644406/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com