Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/153.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为了在内存不足的情况下提供异常安全性,降低代码的可读性是否值得?_C++_Exception_Out Of Memory_Readability_Exception Safety - Fatal编程技术网

C++ 为了在内存不足的情况下提供异常安全性,降低代码的可读性是否值得?

C++ 为了在内存不足的情况下提供异常安全性,降低代码的可读性是否值得?,c++,exception,out-of-memory,readability,exception-safety,C++,Exception,Out Of Memory,Readability,Exception Safety,我有一个不可复制项目的游戏,因为它们应该是独一无二的: class Item { Item() noexcept; Item(Item&&) noexcept; Item(Item const&) = delete; // ... }; 职业生物能够接收物品并将其添加到其库存中: 这种方法看起来很好也很干净,但有一个小问题:如果我的代码的任何部分在std::bad_alloc之后可以恢复,因此捕获了receive_item的push_back抛出的一个,那么

我有一个不可复制项目的游戏,因为它们应该是独一无二的:

class Item {
  Item() noexcept;
  Item(Item&&) noexcept;
  Item(Item const&) = delete;
// ...
};
职业生物能够接收物品并将其添加到其库存中:

这种方法看起来很好也很干净,但有一个小问题:如果我的代码的任何部分在std::bad_alloc之后可以恢复,因此捕获了receive_item的push_back抛出的一个,那么游戏中的逻辑是未定义的:一个项被移动到一个函数,而该函数无法存储一个,因此它就丢失了。此外,必须删除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,这掩盖了移动项的事实; resize+1部分很难看,在代码审查期间可能会被误解。
问题在标题中。即使对于这种奇怪的情况,异常安全性也被认为是一种好的设计吗?

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

关于您的代码修改,您不必修改您的呼叫来推回:

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


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

您真的能从std::bad\u alloc中恢复吗?如果在向量中插入单个元素会耗尽内存,我看不出恢复的机会有多大。对于StackExchange上的代码审阅站点来说,这可能是一个更好的问题。@据我所知,NathanOliver向量通常会多分配x1.5或x2,所以我想差异可能会很严重。@通过是,但是,你的生物真的有那么大的库存量吗?如果库存量是原来的两倍,那么它的记忆量就相当可观了吗?@NathanOliver,我真的没料到它会这么做。但是,如果以后我决定需要这种类型的异常安全,那么要调查、完全理解和重写的代码太多了,因此,我不希望在这里延迟做出决定。在这种情况下,我假设std::move是在push_back之前执行的,因为后者将项目作为参数来获取,所以移动似乎只是为了执行实际的函数调用。你确定我错了吗?std::move没有复制或移动任何东西,它只是静态\u cast的语法糖,因此push \u back是用右值引用参数而不是左值引用调用的。哦,真的,这对我来说是显而易见的:我甚至在一分钟前测试过它。谢谢你不小心填补了我知识上的一个漏洞。
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
}
this->inventory.push_back(std::move(item));