为了在发生内存不足错误的情况下提供异常安全性,值得降低我的代码的可读性吗?

问题描述 投票:0回答:1

我有一个带有不可复制的Item的游戏,因为它们应该是唯一的:

class Item {
  Item() noexcept;
  Item(Item&&) noexcept;
  Item(Item const&) = delete;
// ...
};

[class Creature能够接收Item并将其添加到他们的inventory

void Creature::receive_item(Item&& item) noexcept {
  this->inventory.push_back(std::move(item));
}

void caller_code() {
  Creature creature;
  Item item;
  // ...
  creature.receive_item(std::move(item));
}

这种方法看起来不错,也很干净,但是存在一个小问题:如果我的代码的任何部分都可以在std::bad_alloc之后恢复,并因此捕获了recieve_item()push_back()抛出的错误,则游戏内逻辑未定义: Item已移至无法存储一个功能的功能,因此该功能刚刚丢失。此外,仅出于这种可能性就必须删除noexcept说明符。

因此,我可以通过这种方式提供异常安全性(如果我错了,请指出:)

void Creature::receive_item(Item& item) {
  this->inventory.resize(this->inventory.size() + 1);
  // if it needs to allocate and fails, throws leaving the item untouched
  this->inventory.back() = std::move(item);
}

void caller_code() {
  Creature creature;
  Item item;
  // ...
  creature.receive_item(item); // no moving
}

但是,现在我们有新的缺点:

  • 在调用者的代码中缺少std::move(),这掩盖了移动item的事实;
  • “ resize(+1)”部分很丑陋,在代码审查期间可能会被误解。

问题在标题中。即使对于这种奇怪的情况,异常安全性也被认为是好的设计吗?

c++ exception out-of-memory readability exception-safety
1个回答
1
投票

您问题的答案取决于您是否可以并且想要处理内存分配错误,而不是崩溃。如果这样做,则除了捕获std::bad_alloc外,还必须执行其他措施。大多数现代操作系统都实现memory overcommit,这意味着内存分配将成功,但是首次访问分配的内存将导致页面错误,通常会导致崩溃。您必须在将内存返回给调用者之前,在内存分配器中显式地对分配的页面进行故障检查,以检测出内存不足的情况。

关于代码修改,您不必修改对push_back的调用:

this->inventory.push_back(std::move(item));

如果push_back(或任何其他可能的重新分配方法)需要分配新的缓冲区,它将这样做之前

将新项移动到向量中。显然,这是因为向量中没有空间可将新元素移入。当重新分配缓冲区并将所有现有元素移动/复制到新缓冲区时,新元素将作为最后一步被移动。

换句话说,如果抛出push_back,则可以确保新项目尚未移动。如果它没有抛出并正常返回,则该项目将从中移出。

© www.soinside.com 2019 - 2024. All rights reserved.