problem
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 problemdefault: break;}
問題得到解決,在新的約束範圍內,變數 a 很好的工作,而且也正確的實現了意圖。接著方案2:代碼:
switch (t){case 0: int a; a = 0; break;default: break;}
也沒錯。似乎兩個方案都完成了目的,而且等價。於是作者把注意力完全轉移到初始化問題上,糾住不放,到最後也沒講清楚為何有時候可以初始化,有時候確不能。
其實再深入一步,就完全清楚了,我把程式改動了一下,加了幾個列印語句:代碼:
#include <stdio.h>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 的例子,道理一樣。
所謂盡信書不如無書,何況並非大師的手筆,我們看的時候得多長個心眼,多動動手有助於問題分析。