在将旧项目升级到VS2015时,我注意到有很多错误,例如在函数内部重新定义了局部变量。
void fun()
{
int count = applesCount();
cout << "Apples cost= " << count * 1.25;
for (int bag=0; bag<5;bag++)
{
int count = orangesCount(bag);
cout << "Oranges cost = " << count * 0.75;
}
}
编译器的错误/警告消息是:
declaration of 'count' hides previous local declaration
我知道对变量count
使用相同的名称显然不是一个好习惯,但是编译器是否真的可以解决问题,或者通常他们会优雅地处理这种情况?
更改和修复变量名称是否值得,或者不太可能造成任何伤害,风险很低或没有风险?
答案 0 :(得分:13)
我注意到有很多错误,例如在函数内部重新定义了局部变量。
您未在此处演示重新定义。您将显示变量阴影的示例。
变量阴影在语法上不是错误。它是有效的,定义明确。但是,如果您打算使用外部作用域中的变量,那么您可以将其视为逻辑错误。
但是编译器真的会搞砸了吗
没有
阴影的问题在于,很难跟踪程序员的 。这对于编译器来说是微不足道的。你可以在这个网站上找到很多问题,这些问题源于阴影变量引起的混乱。
要知道哪个表达式在这个小函数中使用哪个变量并不太困难,但想象一下该函数是几十行和几个嵌套和顺序块。如果函数足够长,以至于您无法一目了然地看到不同范围内的所有不同定义,那么您可能会误解。
declaration of 'count' hides previous local declaration
这是一个有点有用的编译器警告。您还没有用完名称,那么为什么不为函数中的所有局部变量赋一个唯一的名称呢?但是,不需要将此警告视为错误。这只是提高程序可读性的建议。
在这个特定示例中,在内部范围打开后,您不需要外部作用域中的count
,因此您也可以为这两个计数重用一个变量。
更改和修复变量名称是否值得
取决于您是否重视短期工作量与长期工作量之间的关系。更改代码以使用唯一的描述性局部变量名称是&#34; extra&#34;现在工作,但每次有人必须在以后理解该程序时,不必要的阴影会增加心理挑战。
答案 1 :(得分:3)
编译器可以识别外部变量和内部变量。
有了良好的词汇量(和词库),人们不需要使用相同的变量名。
答案 2 :(得分:3)
隐藏变量(这就是它)具有完全明确定义的语义,因此编译器不会搞砸它。它将完全按照它所说的方式完成,具有明确的结果。
问题是(通常情况下)与人类有关。在阅读和修改代码时很容易出错。如果一个人不是很小心,那么跟踪哪个变量具有给定名称被引用可能会很棘手,并且在您认为正在修改一个变量时很容易出错,但实际上您正在修改另一个变量。
所以,编译器很好,程序员就是问题。
答案 3 :(得分:1)
根据我的经验,编译器通常会非常优雅地处理这个问题。
然而,这是绝对不好的做法,除非你有一个非常令人信服的理由这样做,你应该重用旧的变量(如果逻辑上有意义的话),或者声明一个[不同名称]的新变量。
答案 4 :(得分:0)
扎尔,
编译器将处理这种情况。在您的示例中,count变量在两个不同的范围“{}”中定义。由于变量的范围,汇编语言将引用堆栈上的两个不同地址。第一个“计数”可能是堆栈点SP-8,而内部计数可能是SP-4。一旦转换成地址,名称就无关紧要了。
由于风格原因,我通常不会更改工作代码。如果代码混乱,那么你就有可能破坏它。通常凌乱的代码没有任何好的测试,所以很难知道你是否打破了它。
如果您需要增强代码,那么肯定要整理一下。
- 亚光
答案 5 :(得分:0)
可能非常糟糕:
考虑此问题
std::vector<int> indices(100);
if (true) {
std::vector<int> indices(100);
std::iota(indices.begin(), indices.end(), 0);
}
// Now indices will be an all-0 vector instead of the an arithmetic progression!
答案 6 :(得分:0)
在 Visual Studio 中,使用警告级别 4 /W4
进行编译,即使通过使用隐式 this
指针作为前缀来消除歧义,例如 this->Count
:
警告 C4458:'Count' 的声明隐藏类成员
尽管编译器很少对这些值犯错,但阴影可能会被滥用并导致超时混淆。
下面是一个应该避免的隐藏 Count 成员变量的例子:
来自 Unreal Coding Standard 文档。
class FSomeClass
{
public:
void Func(const int32 Count)
{
for (int32 Count = 0; Count != 10; ++Count)
{
// Use Count
}
}
private:
int32 Count;
}
解决这个问题的一种方法是在发生阴影时为传入的参数名称添加前缀,如下所示:
class FSomeClass
{
public:
void Func(const int32 InCount)
{
Count = InCount;
for (int32 Counter = 0; Counter != 10; ++Counter)
{
// Use Count
}
}
private:
int32 Count;
}