在函数中使用裸括号是一种好的做法吗?

Is it a good practice to use naked brackets inside functions?

本文关键字:一种 函数      更新时间:2023-10-16

从技术上讲,在C++中,我们可以使用大括号来声明新的作用域。例如,在此函数中,交换两个数字

void swap_int(int& first, int& second)
{
int temp = first;
first = second;
second = temp;
}

我们还可以在它自己的块中声明temp

void swap_int(int& first, int& second)
{
// Do stuf...
{
int temp = first;
first = second;
second = temp;
}
// Do other stuff...
}

这显然具有一个优点,即不再需要temp时会直接删除它。

但是,在我编写的代码中,我从不使用它。此外,在来自第三方库的代码中,我几乎从未见过它。

为什么不公开使用?它是否带来了任何性能提升,或者它只是意味着额外的打字工作?

我认为裸括号本身没有任何问题。它们是语言的一部分,并且定义明确。从历史上看,我发现它们有用的一个地方是使用使用状态代码而不是异常的代码,同时保持常量的优点:

const StatusCode statusCode = DoThing();
if (statusCode == STATUS_SUCCESS)
Foo();
else
Bar();
const StatusCode statusCode2 = DoAnotherThing();  // Eww variable name.
...

另一种选择是:

{
const StatusCode statusCode = DoThing();
if (statusCode == STATUS_SUCCESS)
Foo();
else
Bar();
}
{
// Same variable name, used for same purpose, easy to
// find/replace, and has const guarantees. Great success.
const StatusCode statusCode = DoAnotherThing();
...
}

这同样适用于使用 RAII 的线程锁等对象(互斥对象、信号量等),或者通常您希望生存期极短的任何类型的资源(例如文件句柄)。

就个人而言,我认为它很少见的原因是它可以指示代码气味(尽管并非总是如此)。如果存在裸括号,则可能有机会分解出函数。

举个例子,如果swap_int有多个作业,那么该函数正在做不止一件事。通过将实际的交换代码提取到另一个函数中,您可以鼓励重用!例如:

template <typename T>
void swap_anything(T &first, T& second)
{
T temp = first;
first = second;
second = temp;
}
// -------------------------------------------
void swap_int(int& first, int& second)
{
// Do stuff...
swap_anything(first, second);
// Do other stuff...
}

有时这是很好的做法(虽然我不喜欢这个词,因为它是主观的和特定于上下文的),但就像生活中的许多事情一样,把它极端化(在光谱的两端)是一个坏主意。

您会看到有时在 RAII 很重要的C++中引入新的作用域,例如在处理线程锁时。有时,创建和销毁对象的确切时刻非常重要,需要控制。在这些情况下,引入新作用域是实现此目的的一种非常有用且经常使用的方法。

但这种情况并不常见。我们(广泛的编程社区)使用的大多数对象都没有需要如此仔细管理的严格生存期。因此,不值得随意引入新的作用域来管理其生存期不值得微观管理的对象的生存期。

如果这样做,就会降低信噪比,人们将很难分辨引入哪些示波器来仔细控制重要资源,哪些示波器不是。这使得在跨团队重构或开发代码时很容易引入错误。至少,你会让编程变得更加乏味,这很糟糕,如果可以的话,你通常应该避免这样做,否则暴力精神病患者可能会把它发泄在你身上。

相关文章: