2013-01-08 71 views
2

我有一個頭文件我想包含在另一個cpp文件中。我想知道有什麼區別,如果我寫這樣的頭文件,通過包含訪問函數vs聲明靜態

#include <iostream> 
#include <string> 
using namespace std; 

string ret() 
{ 
return "called"; 
} 

========================== =========

#include <iostream> 
#include <string> 
using namespace std; 

static string ret() 
{ 
return "called"; 
} 

無論如何我可以訪問ret()函數!那麼,靜態的用途是什麼?

+1

這是一個非常糟糕的.h由於幾個原因 –

+0

我只是寫它來簡化我的問題。但它有什麼不好? – Tahlil

+2

[什麼是「靜態」函數?](http://stackoverflow.com/questions/558122/what-is-a-static-function) –

回答

4

第一個頭文件定義了一個叫做ret的函數,在包含它的每個翻譯單元中都有外部鏈接。如果在同一個程序中鏈接了多個這樣的TU,則這是不正確的。

第二個頭文件定義了一個名爲ret的函數,在包含它的每個翻譯單元中都有內部鏈接。這意味着無論有多少個鏈接在一起,每個TU都有自己的私人副本(具有不同的地址)。

有三種正確的方式來分享的代碼中使用的頭文件:

  • 函數具有內部鏈接(如在第二個報頭,或在C++ 11通過把它在一個無名命名空間)。
  • 帶外部鏈接的內聯函數(將static替換爲inline)。 inline的含義是,雖然程序中只有一個函數副本,但使用該函數的每個TU都包含其定義。
  • 在頭文件中聲明函數,並在其中定義一個.cpp文件(例如ret.cpp)。

在C++ 03出現了第四種方式:

  • 與外部聯動功能在一個無名的命名空間

我相信這仍然是C++ 11獲得,但在C++中,無名稱命名空間中的11個函數默認具有內部鏈接。我沒有意識到在C++ 11中用於使無名稱名稱空間中的函數具有外部鏈接的用處。就功能而言,無名的名字空間是賦予函數內部鏈接的一種很好的方式。

您使用哪一個取決於您的需求。第三個選項意味着你可以在不重新編譯調用代碼的情況下更改函數的定義,儘管你仍然需要重新鏈接可執行文件,除非函數在dll中。

前兩個(staticinline)在他們的行爲有所不同,如果:

  • 函數包含static局部變量,
  • 你比較函數指針在不同的TU採取ret
  • 您檢查您的可執行文件大小或符號表,
  • 函數的定義在不同的TU中可能不同(可能是由於不同的#define),如果函數ha外部聯繫,但不是內部聯繫。

否則它們大致相同。

根據標準,inline也暗示編譯器應該優化對該函數的調用以便快速執行(實際上這意味着將內聯代碼放入調用站點)。大多數編譯器大部分時間都忽略這個提示。如果他們評估該函數是內聯的一個好候選者,他們會很高興地嵌入static函數,但如果他們評估該函數是內聯的一個不好的候選者,他們會高興地避免內聯函數。

+0

+1這幾乎總結得很好,讓我刪除我的答案。 –

+0

你能解釋一下「外部/內部聯繫」是什麼意思? – Tahlil

+0

@kalkin:我的意思是與C++標準意義相同的東西。具有外部鏈接的名稱指的是相同的代碼實體(函數或對象),無論它使用什麼TU。帶有內部鏈接的名稱是指每個TU中的不同代碼實體。 –

5

這是一個非常邪惡的頭文件,你顯示。

  1. 決不using namespace std;成一個頭文件。這會強制包括標題的任何人在全局命名空間中擁有全部std

  2. 使用某種形式的包含警衛。

  3. static使該功能在.cpp以外的地方不可見。這意味着包含標題的每個.cpp都有自己的函數副本。 static(非成員)函數只應在您特別需要此行爲時使用。

  4. 如果您不使用static,您應該將標題中的定義移動到源文件中(如果您希望定義一次)或聲明功能inline(其代碼將在每次調用時都內聯網站,如果可能的話)。如果您不這樣做,如果將標題包含在多個源文件中,則會出現多個定義錯誤。

+0

我會給這個+1 *除了*爲第4點),這是...更奇怪嗎? – DevSolar

+0

@DevSolar爲什麼?該功能在標題中定義。如果你將它包含在多個源文件中,你會得到一個「多重定義符號'ret」的鏈接器錯誤或者其他的錯誤。 – Angew

+2

解決方法是*不*定義頭部中的函數,*或*使其靜態/將其置於匿名命名空間中,如果您確實需要*這種行爲。但是爲了避免多重定義錯誤而聲明它是「inline」的是一個醜陋的kimdge IMNSHO。你不僅可以在每個翻譯單元中獲得函數的代碼,而且可以在每個被調用的地方得到這個函數的代碼,即比使用靜態代碼更糟糕...... – DevSolar

2

使用標頭警衛。

不要在頭文件中使用「using namespace」。 (實際上,不要在頭文件中使用「使用」,使用的標識符完全合格的。)

,並使用標頭聲明功能,而不是定義他們。您會希望ret()的代碼僅出現在生成的可執行文件中一次。將ret()的定義(代碼)放在.cpp文件中即可實現此目的。 一個 .cpp文件,不是多個(通過包括定義)。

頭文件中列出的功能ret()以便其他碼「知道」函數存在,它需要哪些參數,並返回什麼的聲明

1

如果在頭文件中將C++方法定義爲static,則每個翻譯單元(每個翻譯單元)包含該頭文件的cpp文件)將具有不同版本的這些靜態方法 - 它們將不具有相同的地址空間。 因此,您的程序的大小將不必要地增加。

而且,只是爲了清晰:

僅在.cpp文件中定義的方法爲static意味着該方法具有靜態鏈接,並從同一.cpp文件中的其他方法才能訪問。