Chinaunix首页 | 论坛 | 博客
  • 博客访问: 343588
  • 博文数量: 97
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 636
  • 用 户 组: 普通用户
  • 注册时间: 2014-10-12 22:41
文章分类

全部博文(97)

文章存档

2017年(8)

2015年(87)

2014年(2)

我的朋友

分类: Windows平台

2017-03-01 19:34:17

The following is not allowed:

switch (a)
{
    case 1:
        int a = 6;
        //stuff
        break;

    case 2:
        //stuff
        break;
}


The following is allowed:

switch (a)
{
    case 1:
        {
            int a = 6;
            //stuff
        }
        break;

    case 2:
        //stuff
        break;
}


来自实际项目的一段代码,简化形式如下:
switch (t)
{
case 0:
int a = 0;
break;
default:
break;
}
有什么问题吗?似乎没有。请用编译器编译一下……
嗯?!一个错误“error C2361: initialization of 'a' is skipped by 'default' label”。这怎么可能?
几番思琢,悟出解释:C++约定,在块语句中,对象的作用域从对象的声明语句开始直到块语句的结束,也就是说default标号后的语句是可以使用对象a的。如果程序执行时从switch处跳到default处,就会导致对象a没有被正确地初始化。确保对象的初始化可是C++的重要设计哲学,所以编译器会很严格地检查这种违例情况,像上述的示例代码中default语句后面并没有使用a,但考虑到以后代码的改动可能无意中使用,所以一样被封杀。
明白了原因,解决起来就很容易了。只要明确地限制对象a的作用域就行了。
switch (t)
{
case 0:
{ //added for fix problem
int a = 0;
break;
} //added for fix problem
default:
break;
}
如果确实需要在整个switch语句中使用对象a,那就把int a = 0;移到switch语句之前即可。不过从原先的语句看,其意图似乎并不是这样的,所以推荐前面的解决方案。


结束了吗?没有。让我们继续考究错误提示信息中“initialization”(也就是初始化)的确切含义。C++很看重初始化,所以往往会给我们造成一种错觉,似乎对象在定义处一定会经过初始化过程。真实情况如何呢?还是用实例来证明吧。
switch (t)
{
case 0:
int a;
a = 0;
break;
default:
break;
}
编译,这次没有报错。很明显int a;定义了对象,但没有进行初始化,否则就应该报告原先的错误。
再看看用户自定义类型。
class B
{
};

switch (t)
{
case 0:
B b;
break;
default:
break;
}
编译结果也没有错误,所以没有提供构造器的类仍然没有初始化过程。
如果给类加入构造器,情况就不同了。
class B
{
public: //added for initialization
B(){} //added for initialization
};
这样就能重现原先的错误。证明有了构造器,编译器就将进行初始化处理并对之进行安全检查。


从上面的实验,可以直观地体验到一些基本的C++观念和原理,并提高认识深度。
1.int a = 0;既是声明也是定义,还包括初始化;int a;是声明还是定义依上下文而定,但如果是定义就不会包括初始化;a = 0;仅仅是赋值语句,在此句前对象已经存在了。
2.为了避免不必要的开销,默认情况下,即程序员没有在代码中明确指示时,编译器不提供初始化过程。某些需要确保初始化的类,请提供构造器。这里透露出一个C++的设计哲学:通常你会面对多种选择,所以请精确地控制代码,其收益则是可以自由取舍调配的安全性、速度、内存开销等程序特性。
3.严密注意程序中标号的使用情况,特别是case、default等常规标号,否则他们可能会破坏对象的正确状态。如果提供了对象初始化,则能够获得编译器的额外帮助。

问题:
1>
"
如果程序执行时从switch处跳到default处,就会导致对象a没有被正确地初始化。确保对象的初始化可是C++的重要设计哲学,所以编译器会很严格地检查这种违例情况,像上述的示例代码中default语句后面并没有使用a,但考虑到以后代码的改动可能无意中使用,所以一样被封杀。
"
它还会考虑到以后代码的改动可能无意中使用这种情况吗?

2>
class B
{
};
为什么声明一个B对象时作者说没有初始化,类不是在没有声明构造函数时会用它的默认构造函数来初始化对象吗?为什么作者说没有初始化呢?

3>
为什么说没有初始化就会出问题呢?在C++中所有对象一定要初始化?不是这样的吧.
作者好像就这个意思啊.!

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

 

首先要承认作者提出这个问题很好,很多人都会碰上的这种情况,但是他本身对这个问题的理解个人认为存在误区或者说不到位。
下面是我的分析:
代码:
		
switch (t) { case 0: int a = 0; break; default: break; }
g++ 下的错误提示是:
case_init.cpp:9: error: jump to case label
case_init.cpp:7: error: crosses initialization of `int a'
首先在这里作者提到问题出在本地(local)变量或者叫做自动变量的作用域范围,点的很好。本地变量的作用域仅在花括号之间。于是方案1:
代码:
		
switch (t) { case 0: { //added for fix problem int a = 0; break; } //added for fix problem default: break; }
问题得到解决,在新的约束范围内,变量 a 很好的工作,而且也正确的实现了意图。接着方案2:
代码:
		
switch (t) { case 0: int a; a = 0; break; default: break; }
也没错。似乎两个方案都完成了目的,而且等价。于是作者把注意力完全转移到初始化问题上,纠住不放,到最后也没讲清楚为何有时候可以初始化,有时候确不能。
其实再深入一步,就完全清楚了,我把程序改动了一下,加了几个打印语句:
代码:
		
#include int main() { printf("请输入一个数字: "); int t; scanf("%d", &t); switch (t) { case 0: int a; printf("/n a = %d/n", a = 0); break; case 1: printf("/n a = %d/n", a = 1); break; default: printf("/n a = %d/n", a = t); break; } }
编译通过,然后运行一下看看,一切正常。可能有人会很奇怪, int a 是在 case 0 中声明的呀!如果运行时选择 1,不就跳过去了吗?怎么在 case 1 和 default 中也能用呀? 殊不知不管你是有没有选 0, 这样定义的局部变量 a 在它的作用域(从声明处到结束花括号)范围之内 都有效。 到这里该明白编译器为何对 int a = 0; 初始化作出反应吧! 虽然你自己知道只在 case 0 中用到 a,在 case 1 或 default 中都不会用到 a(非上例),但编译器不懂你的心思, 为了避免在其它地方用到该变量出现不必要的麻烦,做一个出错提醒。 至于下面这个 class 的例子,道理一样。 所谓尽信书不如无书,何况并非大师的手笔,我们看的时候得多长个心眼,多动动手有助于问题分析。
阅读(1826) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~