首页 > 解决方案 > Is it worth making my code less readable for providing exception safety in case of out of memory errors?

问题描述

I have a game with non-copyable Items as they are supposed to be unique:

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

class Creature is capable of receiving an Item and adding it to their 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));
}

This approach seems nice and clean but there's a small problem: if any part of my code can recover after std::bad_alloc and therefore catches one thrown by recieve_item()'s push_back(), the in-game logic is undefined: an Item was moved to a function which failed storing one so it is just lost. Moreover, the noexcept specifier has to be removed just for this possibility.

So, I can provide exception safety this way (please point it out if I'm wrong):

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
}

However, now we have new disadvantages:

The question is in the title. Is exception safety even for such a weird case considered a good design?

标签: c++exceptionout-of-memoryreadabilityexception-safety

解决方案


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

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

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

如果push_back(或任何其他可能的重新分配方法)需要分配一个新缓冲区,它将在将新项移动到向量中之前执行此操作。显然,这是因为向量中没有空间可以移动新元素。并且当缓冲区被重新分配并且所有现有元素被移动/复制到新缓冲区时,新元素将作为最后一步移动。

换句话说,如果push_back抛出,您可以确定新项目没有被移动。如果它没有抛出并正常返回,则该项目被移出。


推荐阅读