2010-12-08 54 views
3

根據我的老師的實現,這是不好的做法,編寫用戶定義函數是這樣的:宣言和功能

int DoubleNumber(int Number) 
{ 
    return Number * 2; 
} 

int main() 
{ 
    cout << DoubleNumber(8); 
} 

相反,他說要總是使用前向聲明,即使功能別噸需要對方的任何知識:

int DoubleNumber(int Number); // Forward declaration. 

int main() 
{ 
    cout << DoubleNumber(8); 
} 

int DoubleNumber(int Number) // Implementation. 
{ 
    return Number * 2; 
} 

我發現這非常奇怪,因爲他做了告訴我們是多麼重要的是,向前聲明和實現是一個點正好相同或你會得到錯誤。如果這是件大事,爲什麼不把它放在main()以上呢?

那麼,同時申報和實施真的是不好的做法嗎?它甚至重要嗎?

+2

老師面臨的問題是他們很少有足夠的時間來練習他們所教的科目,而編程就是其中一個領域,如果你不練習它,你就會失去它。 – 2010-12-08 20:03:05

+0

如果您的函數要在編譯單元之外使用,則無論如何您都需要在* .h文件中聲明它們。同樣,非平凡類方法將在類定義中分別聲明。與上述用法相比,IME僅限於內部非內聯函數,因此從廣義上講,它並不重要。 – comingstorm 2010-12-08 20:19:58

回答

9

如果您沒有聲明前向聲明(「原型」),那麼您需要確保所有函數都發生在依賴於它們的函數之前,即按照調用圖的相反順序。對於上面的一個簡單示例來說,這很好,但是對於更現實的任何事情(如果調用圖中存在任何循環的話,在某些情況下不可能),這是完全痛苦的。

+7

我發現重新排序功能比保持聲明和定義不同步更麻煩。它還有助於說明設計的層次結構 - 幫助者在頂部,然後是更重要的東西,然後是main()在底部。儘管如此,似乎有些人更喜歡相反的順序。 – 2010-12-08 19:12:17

+0

看起來像在硬核「乾淨的代碼」中(即將幾乎所有東西放在函數中),這些函數會更加痛苦,特別是在大型項目中。再一次,保持原型和實現同步也似乎是一個大項目的煩人。 – Maxpm 2010-12-08 19:29:58

+0

@Karl:如果你的函數是通過頭文件公開的,那麼無論如何你都必須做「保持同步」。關於個人對訂單的偏好,我的幫手應該放在底部,因爲它們是最沒有趣味的! (至少從「瞭解結構」的角度來看) – 2010-12-08 20:44:49

6

你的老師的政策是可怕的恕我直言。只有在真正需要時才使用前向聲明。這樣,他們的存在證明了他們的必要性,這爲讀者提供了有用的文檔(即,函數之間可能存在相互遞歸)。當然,你需要在頭文件中進行前向聲明;這就是他們的目標。

2

問你的老師爲什麼他建議這樣做;)無論如何,在我看來,這不是壞習慣,在大多數情況下,它甚至都不重要。預先聲明所有函數的最大優點是您可以高級地瞭解代碼的功能。

4

在我的第一個編程課上,老師也強調了這一點。我不確定在實際軟件中有這樣一個簡單情況的好處。

但是,它確實準備使用頭文件,如果你還沒有覆蓋。在典型情況下,您將擁有一個頭文件custom-math.h和源文件custom-math.cpp,其中custom-math.h包含前向聲明和custom-math.cpp的實現。這樣做可能會在僅在大型項目中對函數實現進行修改時顯着增加編譯時間。這也是將程序分成「邏輯」功能組和/或類組的一種便捷方式。

如果你打算把其他功能,在同一個文件main(),然後你做什麼可能取決於你的個人喜好。有些人更喜歡靠近頂部的main()來馬上進入程序邏輯。在這種情況下,向前聲明你的功能。

1

傳統上你會把所有的原型都放在一個頭文件中,這樣他們就可以被其他源文件使用 - 或者至少你會把你想要公開的文件放在.h文件中。

至於沒有必要的代碼,將所有的文件級聲明放在最上面(變量和函數)是有意義的,因爲這意味着你可以隨意移動函數而不必擔心關於它。更何況,我可以立即看到文件中的每個功能。但這樣的:

void Func1() { ... } 
... 
void Func2() { ... } 
... 
void Func3() { ... } 
... 
int main() { Func1(); Func2(); Func3(); return 0; } 

這 - 這就是說,一些不連貫的功能全部由被稱爲主() - 是一種很常見的文件,這是完全合理的放棄聲明。

3

Karl Knecthel寫道:「只有在真正需要時才使用前向聲明,這樣,他們的存在證明了他們的必要性,這爲讀者提供了有用的文檔(即函數之間可能存在相互遞歸)。和恕我直言,這是很好的建議。

奧利查爾斯沃思談到排序函數的「完全痛苦」,以便可以在沒有前向聲明的情況下調用它們。這不是我的經歷,我無法想象這種痛苦/問題是如何實現的。我懷疑那裏存在PEBCAK問題。

對所有函數使用前向聲明的做法不會爲您節省PEBCAK問題,但它們確實引入了不必要的維護工作,並且不需要更多的代碼來關聯,而且它們使得更加不清楚哪些函數確實需要前向聲明。

如果您已經到了前向聲明可以幫助您一眼看到函數簽名的地步,那麼當被迫使用一些非常簡單的編輯器時,應該採取兩個操作:(1)重構代碼,以及(2)切換到更好的編輯器。

乾杯&心連心,

1

毯子規則很少是正確的。您通常會通過頭文件中的原型公開api公開的API。其餘的函數可能會在cpp文件中的匿名命名空間中。如果在實現中多次調用它們,那麼在頂部提供原型是有意義的,否則使用它們的每個函數都必須在調用函數之前提供原型。同時,如果在cpp文件中多次使用某個函數,這可能表明它足夠通用,可以移動到通用api。如果這些功能沒有在整個地方使用,最好提供儘可能有限的曝光,即聲明和定義它們靠近它們被呼叫的地方。

0

就我個人而言,我只想轉發客戶端代碼需要知道的任何內容(即在類頭中)。任何對類實現本地化的東西(比如幫助函數)都應該在使用之前定義。

當然,在一天結束時,大多數情況下,它歸結爲您的項目遵循的個人偏好或編碼標準。

6

我認爲你的老師是一位老C程序員。

如果你編寫了一個沒有前向聲明的C程序和一個名爲稍後在文件中聲明的另一個函數(或者在另一個編譯單元中),編譯器不會抱怨,而是默默地假裝知道原型應該是什麼。

如果您不知道您的編譯器是否正確傳遞參數,那麼調試是非常可怕的。因此,總是宣佈所有功能是一個很好的防守策略;如果聲明與實現不匹配,至少編譯器可能會引發錯誤。

C編譯器和工具變得更好了(我希望)。調用一個未知的函數仍然沒有錯誤,但是例如GCC默認情況下會提示警告。

但在C++中,您無法調用尚未聲明或定義的函數。因此,C++程序員不必擔心前向聲明。