2012-10-26 101 views
10

所以我正在學習課程,我偶然發現了一些我覺得很尷尬的東西。在使用之前,類函數/變量是否必須聲明?

class Nebla 
{ 
    public: 
     int test() 
     { 
      printout(); 
      return x; 
     } 

     void printout() 
     { 
      printout2(); 
     } 

    private: 
     int x,y; 
     void printout2() 
     { 
      cout<<"Testing my class"; 
     } 
}; 

我發現,在I類可以使用的功能之前,我宣佈他們(原型他們)

你可以看到我decleration之前使用printout()printout2()

而且我還可以使用變量聲明它們

之前,你可以看到我做return x;在聲明x之前。

爲什麼我可以在聲明之前在類中使用函數和變量,但如果我這樣做,我會得到一個錯誤?

感謝

+0

我還沒有看到這個問題的具體答案,但我猜想這是由於編譯器在課堂上做了兩遍。 – chris

+0

@chris你是什麼意思,編譯器在類上做兩次傳遞 –

+0

[This comment](http://stackoverflow.com/questions/2632601/why-are-forward-declarations-necessary#comment2646159_2632601)似乎加強了那個猜測。這個問題稍微處理了這個概念。 – chris

回答

13

好問題;多年來我都沒有考慮過這個功能。我翻閱了幾本C++書籍以找到答案,包括Stroustrup的The C++ Programming Language and The Annotated C++ Reference Manual,但沒有人承認或解釋這種差異。但是,我想我可以通過它來推理。

我相信你的榜樣的原因是你的testprintout的身體並不真正出現在你的文件中。該代碼

class MyClass { 
    void someFun() { 
    x = 5; 
    } 
    int x; 
}; 

...這似乎違反了其在使用之前聲明變量的規則,實際上相當於:

class MyClass { 
    void someFun(); 
    int x; 
}; 

void MyClass::someFun() { 
    x = 5; 
} 

一旦我們重寫它這樣,很明顯MyClass定義中的內容實際上是聲明的列表。這些可以以任何順序。在宣佈聲明之前,您不依賴x。我知道這是真的,因爲如果你想重寫這樣的例子,

void MyClass::someFun() { 
    x = 5; 
} 

class MyClass { 
    void someFun(); 
    int x; 
}; 

......它將不再編譯!因此,類定義首先(帶有完整的成員列表),然後您的方法可以使用任何成員,而不考慮它們在類中聲明的順序。

最後一塊難題是C++禁止在類定義之外聲明任何類成員,因此一旦編譯器處理了你的類定義,它就知道類成員的完整列表。這是在Stroustrup's 附註170頁註釋的註釋C++參考手冊:「成員列表定義了該類的全部成員,沒有成員可以添加到別處。

謝謝你讓我調查一下;我今天學了些新東西。 :)

+0

令人驚歎的答案,你100%肯定這個答案是正確的? –

+0

感謝您的讚揚。我確信我寫的是什麼,如果你擔心標準和編譯器的兼容性,這可能不夠好。我認爲aschepler的答案比我的「100%肯定」。 – Philip

+0

是的,這兩個很好的答案,但我更喜歡你的:D,你解釋它。 –

0

你能夠做到這一點的原因是因爲你打電話testprintoutprintout2的時候,他們將已創建。如果在執行任意函數之前調用以外的函數,那麼您將得到一個錯誤。

認爲類成員函數與其他類的評估流程是異步的。這不適用於獨立功能,但您可以訪問尚未實例化的數據成員。我不完全確定爲什麼我們能夠做到這一點,但我認爲它與瞬時對象類型有關。

+0

你能提供更多/更清晰的解釋嗎? –

+0

讓我看看我正確理解這一點, 是因爲所有的函數/變量都是在聲明對象後聲明的嗎? –

4

爲了說清楚,這是C++標準所要求的,而不僅僅是幾個編譯器處理類定義的方式。

N3242 3.3.7:

在一個類中聲明的名稱的潛在範圍不僅包括以下聲明的名稱的點聲明區,而且所有功能機構,大括號或 - 非靜態數據成員的相等初始化程序,以及該類中的默認參數(包括嵌套類中的這些內容)。

1

除了菲利普的反應良好,斯特勞斯給名稱查找規則的C++設計與演變一個很好的解釋。這在「6.3說明」中有所描述。在6.3.1.1,「ARM的名稱查找規則」,他提到在ARM定義2個規則:

[1]類型重新定義規則:A型名稱可能不會在一類後,重新定義已經在那裏使用了。

[2]重寫規則:對內聯定義的成員函數進行分析,就好像它們是在類聲明結束後立即定義的一樣。

所以在你的情況下,它會應用重寫規則(菲利普推論),這就是爲什麼你可以轉發引用這些類成員。

這本書可能主要是歷史利益(它寫在'94),但我認爲這些規則今天應用相同的方式。