利弊分析:在条件语句中初始化变量

20

C++中,您可以在if语句中初始化变量,例如:

if (CThing* pThing = GetThing())
{
}
为什么有人认为这样做是好的或者不好的风格?这样做有什么优点和缺点?
个人认为这种风格不错,因为它限制了pThing变量的范围,所以当它为NULL时就不会被意外使用。但是,我不喜欢你不能这样做的情况:
if (CThing* pThing = GetThing() && pThing->IsReallySomeThing())
{
}

如果有办法使上述内容可行,请发布。但如果那不可能,我仍然想知道为什么。

这里借鉴了一个问题,类似的主题,但是PHP。

12个回答

22

重要的是,在C++中,声明不是表达式。

bool a = (CThing* pThing = GetThing()); // not legit!!

在if语句中,你不能同时进行声明和布尔逻辑操作。C++语言规范明确允许使用表达式或声明。

if(A *a = new A)
{
    // this is legit and a is scoped here
}

在一个表达式中,我们如何知道是否定义了 a 这个变量,且它在两个项之间呢?

if((A *a = new A) && a->test())
{
    // was a really declared before a->test?
}

硬着头皮使用内部if语句。范围规则很有用,你的逻辑也很明确:

if (CThing* pThing = GetThing())
{
    if(pThing->IsReallySomeThing())
    {
    }
}

1
"(A *a = new A) && a->test()" 这不是标准的 C++。(-1) - Richard Corden
我会接受这个答案,因为它在技术上非常详尽。关于哪种更好的讨论确实高度主观,我更喜欢我最初发布的风格,但是我的TD不希望我使用它,所以我不会使用。作为妥协,我可以始终使用额外的括号来限制范围。 - steffenj

4

关于优势:

建议在首次需要变量时定义它们,而不是在一行之前。这样可以提高代码的可读性,因为可以在不滚动和搜索定义位置的情况下了解 CThing 是什么。

同时,将范围缩小到循环/条件语句块中,使得在代码块执行后未引用的变量成为垃圾回收的候选对象(如果语言支持此功能)。


4
自从C++17以来,您可以在ifswitch语句中使用初始化语句。
以下是更新后的代码示例:
if (CThing* pThing = GetThing(); pThing->IsReallySomeThing())
{
    // use pThing here
}
// pThing is out of scope here

3
if (CThing* pThing = GetThing())

这是if语句的CThing*不是布尔表达式,风格很差。

CThing* pThing = GetThing();
if (pThing != NULL)

这是很好的样式。


2

有很多事情需要注意,首先是裸指针。请尽一切可能避免使用它们,改用引用、optional、unique_ptr、shared_ptr。如果万不得已,请编写处理指针所有权而不涉及其他内容的自定义类。

如果可以要求使用C++11(最好避免C++11的缺陷),请使用统一初始化:它避免了=与==混淆,并在检查参数时更加严格。

if (CThing thing {})
{
}

请确保实现operator bool以获得从CThing到bool的可预测转换。但是,请记住其他人阅读代码时可能不会立即看到operator bool。显式方法调用通常更易读和令人放心。如果可以要求使用C++17,则使用初始化语法。

if (CThing thing {}; thing.is_good())
{
}

如果无法使用C++17,请按照其他人建议的,使用if语句前的声明。
{
  CThing thing {};
  if (thing.is_good())
  {
  }
}

2

我通常不这样做的一个原因是因为在条件测试中错过了'='号,这是一种常见的错误。我使用带有错误/警告设置的lint来捕获这些错误。它会对条件语句中的所有赋值进行警告。


2
这在C++中应该不会起作用,即使它支持短路求值也许不要尝试以下操作:
if ((CThing* pThing = GetThing()) && (pThing->IsReallySomeThing()))
{
}

嗯...看一下Wesley Tarle的回答

我会尝试……我记得我尝试过各种括号的放置方式,但是没有一种能够正常工作,虽然编译器错误随着括号的变化而改变了很多。 - steffenj
"短路求值"...我之前只知道它叫做“提前结束”。这个新名词我还不熟悉。 - steffenj

2

提醒一下,一些较旧的微软C++编译器(如Visual Studios 6和.NET 2003)在某些情况下未完全遵循作用域规则。

for(int i = 0; i > 20; i++) {
     // some code
}

cout << i << endl;

我应该不在范围内,但那是/仍然是有效的代码。我认为它被打包成一个功能,但在我看来,它只是不遵守规范。不遵守标准是不好的。就像一个Web开发人员询问IE和Firefox一样。

能否有VS的人检查并查看是否仍然有效?


1
这是一个编译器选项,“在for语句中强制执行作用域”或其他什么。 - Martin Beckett
2
没错,它是 /Zc:forScope- "强制符合 for 循环作用域"。至少在 VS 2008 中是这样。 - DK.
谢谢大家的查看。我猜默认情况下它是打开(破坏作用域)的吗? - J.J.

0

你也可以在赋值语句外面加上一组额外的括号 ( ),以避免警告信息。


0
还要注意,如果你在写C++代码,你需要让编译器将条件语句中的“=”(不是声明语句的一部分)警告作为错误处理。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接