日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

函数实现不放在头文件的原因,及何时可以放头文件的情况

發布時間:2024/4/11 编程问答 59 豆豆
生活随笔 收集整理的這篇文章主要介紹了 函数实现不放在头文件的原因,及何时可以放头文件的情况 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1?、引子
???????在平常的?C/C++?開發中,幾乎所有的人都已經習慣了把類和函數分離放置,一個?.h?的頭文件里放聲明,對應的?.c?或者?.cpp?中放實現。從開始接觸,到熟練使用,幾乎已經形成了下意識的流程。盡管這樣的做法無可厚非,而且在不少情況下是相對合理甚至必須的,但我還是要給大家介紹?一下把實現全部放置到頭文件中的方式,給出可供大家使用的另一個選擇。同時針對這一做法,也順便說一下其優缺點以及需要注意的情況。
???????我是一個很喜歡簡潔的人,多年以來甚至養成了這樣的癖好,如果一個功能是能夠用一條語句實現的,那就不要用兩條語句。在我看來,如果給別人提供一份可以復?用的代碼的話,最優雅的狀態莫過于僅僅提供一個頭文件就全部搞定。之所以不太喜歡引入源文件,最重要的原因是源文件往往會帶來工程文件的變化;而且,在使?用過程中也會增加一些額外的操作,例如,在一個組織良好的工程里,頭文件和源文件很有可能是位于不同的目錄,這樣就會多帶來一次文件復制操作。

2?、正文
?????2.1?顧慮
?????????我遇到有不少人不使用頭文件來包含實現,往往是出于以下幾種顧慮:
?????????1、?暴露了實現細節
?????????2、?頭文件被包含到不同的源文件中,會導致鏈接沖突
?????????3、?頭文件被包含到不同的源文件中,會導致有多份實現被編譯出來,增大可執行體的體積
???????如果有顧慮?1?,那很顯然應該在第一時間拋棄完全在頭文件中實現的念頭。不過我遇到的情形里,通常后兩種顧慮占據了絕對的比例。而這種顧慮,通常是由于對?C/C++?沒有足夠的了解導致的。
??????有顧慮?2?的,經常會是一些有?C?語言開發經驗的程序員。他們所擔心的也往往是出現的全局函數的情況。例如有以下頭文件?c_function.h?(清晰起見,防衛宏之類的代碼沒有列出):
[cpp]
int?integer_add(const?int?a,?const?int?b)?
{?
?????????return?a?+?b;?
}?
??????如果在同一工程中,有?a.c?(或者是?.cpp?)和?b.c?兩個(或兩個以上)源文件包含了此頭文件,則在鏈接時期就會發生沖突,因為在兩個源文件編譯得到的目標文件中都有一份?integer_add?的函數實現,導致鏈接器不知道對于調用了此函數的調用者,應該使用哪一個副本。

???????2.2?著手
???????解決的辦法有兩個,各自為兩個關鍵字,一個是?inline?,另一個是?static?。使用這兩個關鍵字的任意一個來修飾?integer_add?函數,都會消除上述的沖突問題,然而本質卻大不相同。
???????如果使用?inline?,則意味著編譯器會在調用此函數的地方把函數的目標代碼直接插入,而不是放置一個真正的函數調用,實際作用就是這個函數事實上已經不再存在,而是像宏一樣?被就地展開了。使用?inline?的副作用,首先在于毋庸置疑地,代碼的體積變大了;其次則是,這個關鍵字嚴格算起來并不是?C?語言的關鍵字,使用它多少會帶來一些移植性方面的風險,盡管主流的?C?語言編譯器都可以支持?inline?。對于?GCC?,?inline?功能關鍵字就是?inline?本身,而對于微軟的編譯器,應該是?__inline?(注意有兩個前導下劃線)。而且,根據慣例,?inline?通常都是對編譯器的某種暗示而非強制要求,編譯器有權力在你不知情的情況下把它實現為非?inline?的狀態(可能的原因有,函數太大或者復雜度過高)。這樣的后果是什么,不好意思,我沒有測試過。
???????如果是使用?static?,那么至少結果是可預料的。所有包含此頭文件的源文件中都會存在此函數的一份副本。雖然代碼也有一定程度的膨脹,但好就好在互相不沖突,因為?static?關鍵字保證了該函數的可見度為單個源文件之內。
以上的討論雖然看起來主要聚焦在?C?語言上,但由于?C++?是?C?語言的超集,并且在這些方面并沒有做太多的修改,因此討論結果同樣也適用于?C++?。

????????2.3?繼續
????????對于?C?語言來講,上面的改進幾乎已經走到了盡頭,沒有繼續發展的余地。然而對于?C++?則不同,我們還可以進一步把它做得更漂亮。
首先,我們做以下的改動:
[cpp]?
class?Integer?
{?
public:?
?????????int?add(int?a,?int?b)?
?????????{?
???????????????????return?a?+?b;?
?????????}?
};?
???????這樣的形式,幾乎連?C++?的初學者都能看出來,確實不會再發生鏈接沖突的問題了。不過也有一個問題,我們如果要計算兩個整數的和的話,需要這樣寫:
???????Integer?op;
???????op.add(i,?j);
???????而這顯然不是一種可接受的狀態,之前很簡單的一條函數語句的調用,現在卻必須定義一個類的對象實例。于是我們再次求助于?static?(?inline?是不適用的,因為它不能去掉定義對象實例這一步,而且事實上,把實現寫到類定義之內的函數缺省就是?inline?的)。現在,類就像這個樣子:
[cpp]
class?Integer?
{?
public:?
?????????static?int?add(int?a,?int?b)?
?????????{?
???????????????????return?a?+?b;?
?????????}?
};?
???????調用方式也相應地簡化為:
???????Integer::add(i,?j);
???????尤其需要注意的就是這里,?C++?類中的?static?函數和全局?static?函數的行為是有差異的,它編譯之后僅產生一份實現代碼,并不會由于被多個源文件包含而產生多份副本?。
???????這距離我們的終極目標已經不遠了(我們的終極目標是:?add(i,?j)?就可以搞定)。于是我們再次高舉起宏這桿大旗,在頭文件里添加以下定義:
???????#define?integer_add?????????Integer::add(后注:突然想到,似乎定義?const?函數指針也可以達到相同的目的)
??????上面解決的其實僅僅是?C++?中全局函數的頭文件復用問題,那么類呢?類的情況要復雜一些。如果是?static?方法,那么正好是和上述我們對全局函數的變通實現是一致的;如果是?inline?的方法(不管有沒有?inline?關鍵字),則其狀態幾乎理論上等同于前面所述的?inline?全局函數的情況。那么還有最后的一種情況,?virtual函數。對于?virtual?函數,我們等到的是一個好消息:它總是生成一份代碼(甚至你顯式使用?inline?關鍵字修飾)?。這里面有個玄機:?virtual?函數的地址會被寫到類的?v-table?里,是要能夠在運行期被調用的(其核心在于,其調用者以及調用時機在編譯時是不明確的),所以絕對不能生成為全部就地展開的形式。以此可以做一個推論:所?有會被求址的成員函數,都會生成一份函數實體,而不能單純地去符合內聯的修飾關鍵字。

3?、后記
??????當然,把實現全部放在頭文件中并不是萬金油,不是放之四海而皆準的準則,正如本文開頭所說,這僅僅是一種選擇,只不過你之前沒有想到過可以這么做,而現在知道了。它最適合的場合是一些規模較小的工具類的實現。

?

總結

以上是生活随笔為你收集整理的函数实现不放在头文件的原因,及何时可以放头文件的情况的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。