在性能、代码大小和惯用的 C++ 方面,什么更有意义:将完整性检查留给函数,还是事先进行检查以避免调用函数?
比较以下代码,我在
reserve
和 insert
中进行了相同的健全性检查。我可以跳过后面直接调用函数。我应该吗?
#include <initializer_list>
#include <cstddef>
struct vector
{
void insert(std::initializer_list<int> ilist) {
if (ilist.size() > capacity_) { // ... sanity check here makes sense?
reserve(ilist.size());
}
// ... or just call it regardless?
// reserve(ilist.size());
// ...
}
void reserve(size_t new_cap) {
if (new_cap < capacity_) {
return ;
}
// ...
}
size_t capacity_;
};
int main()
{
vector v{6};
v.reserve(10);
v.insert({1,2,4,5});
}
我也想用更笼统的术语来讨论这个问题。可能还有我没有考虑到的因素。你会如何处理这种情况?
这完全取决于你想提供什么保证。
比较
std::vector
:
std::vector::insert
并且当前容量不足,向量将为新元素腾出空间。std::vector::reserve
时请求的容量小于当前容量,则该函数不执行任何操作。对于两者,检查不仅仅是“完整性检查”,而且是必需的。
如果你在你的
vector
中删除它们那么你的向量将不同:
insert
假设有足够的容量(这是一个先决条件,如果调用者忽略它,他们会得到他们应得的:未定义的行为)。reserve(x)
确保调用后的容量正好是x
。如果向量大小大于x
,它将相应地进行调整。如果这就是您想要的
vector
,那么支票可以被移除。在发布版本中你不应该做的是检查先决条件。例如,在 operator[]
中进行边界检查,而 operator[]
仅指定用于有效索引。如果调用者忽略前提条件(索引必须有效),那是他们的错。当 operator[]
未明确指定这样做时,遵守前提条件的用户不想为边界检查付费。与具有 std::vector
方法的 at()
进行比较,该方法进行边界检查。
TL;DR:视情况而定。
健全性检查用于查明函数开始前的一个或多个参数、函数内部的值以及函数返回值是否有效以继续进行。例如,在访问向量中的元素时考虑边界检查。这种类型的检查被研究为前置条件、断言和后置条件。这种类型的检查通常用于开发时间,通常在生产中关闭。
你在代码中所说的健全性检查实际上是程序逻辑的一部分,你必须这样做。问题是您的支票应该放在哪里? 作为准则,您应该将密切相关的逻辑放在一起。所以你的方法似乎是正确的。