C++中extern的使用

fengbingchun發表於2017-12-31

在C++中,extern主要有兩個作用:(1)、extern宣告一個變數或函式;(2)、extern與”C”一起連用,用於連結指定。關於extern “C”的使用可以參考: http://blog.csdn.net/fengbingchun/article/details/78634831 ,這裡主要介紹extern宣告一個變數或函式時的用法。

extern可置於變數或者函式前,以表示變數或者函式的定義在別的檔案中,提示編譯器遇到此變數或函式時,在其它模組中尋找其定義。

如果想宣告一個變數而非定義它,就在變數名稱前加關鍵字extern,而且不要顯示地初始化變數。任何包含了顯示初始化的宣告即成為定義。我們能給由extern關鍵字標記的變數賦一個初始值,但是這麼做也就抵消了extern的作用。extern語句如果包含初始值就不再是宣告,而變成定義了。在函式體內部,如果試圖初始化一個由extern關鍵字標記的變數,將引發錯誤變數能且只能被定義一次,但是可以被多次宣告。宣告和定義的區別看起來也許微不足道,但實際上卻非常重要。如果要在多個檔案中使用同一個變數,就必須將宣告和定義分離。此時,變數的定義必須出現在且只能出現在一個檔案中,而其它用到該變數的檔案必須對其進行宣告,卻絕對不能重複定義。

某些時候有這樣一種const變數,它的初始值不是一個常量表示式,但又確實有必要在檔案間共享。這種情況下,我們不希望編譯器為每個檔案分別生成獨立的變數。相反,我們想讓這類const物件像其它(非常量)物件一樣工作,也就是說,只在一個檔案中定義const,而在其它多個檔案中宣告並使用它。解決的辦法是,對於const變數不管是宣告還是定義都新增extern關鍵字,這樣只需定義一次就可以了。如果想在多個檔案之間共享const物件,必須在變數的定義之前新增extern關鍵字

在大系統中,在多個檔案中例項化相同模板的額外開銷可能非常嚴重。在新標準中,我們可以通過顯示例項化(explicit instantiation)來避免這種開銷。一個顯示例項化有如下形式:

extern template declaration; // 例項化宣告
template declaration; // 例項化定義
declaration是一個類或函式宣告,其中所有模板引數已被替換為模板實參。當編譯器遇到extern模板宣告時,它不會在本檔案中生成例項化程式碼。將一個例項化宣告為extern就表示承諾在程式其它位置有該例項化的一個非extern宣告(定義)。對於一個給定的例項化版本,可能有多個extern宣告,但必須只有一個定義。由於編譯器在使用一個模板時自動對其例項化,因此extern宣告必須出現在任何使用此例項化版本的程式碼之前。當編譯器遇到一個例項化定義(與宣告相對)時,它為其生成程式碼。對每個例項化宣告,在程式中某個位置必須有其顯示的例項化定義。一個類别範本的例項化定義會例項化該模板的所有成員,包括內聯的成員函式。當編譯器遇到一個例項化定義時,它不瞭解程式使用哪些成員函式。因此,與處理類别範本的普通例項化不同,編譯器會例項化該類的所有成員。即使我們不使用某個成員,它也會被例項化。因此,我們用來顯示例項化一個類别範本的型別,必須能用於模板的所有成員。在一個類别範本的例項化定義中,所用型別必須能用於模板的所有成員函式

模板顯示例項化(explicit instantiation):一個宣告,為所有模板引數提供了顯示實參。用來指導例項化過程。如果宣告是extern的,模板將不會被例項化;否則,模板將利用指定的實參進行例項化。對每個extern模板宣告,在程式中某處必須有一個非extern的顯示例項化

關鍵字extern為宣告但不定義一個物件提供了一種方法,實際上它類似於函式宣告,承諾了該物件會在其它地方被定義或者在此文字檔案中的其它地方,或者在程式的其它文字檔案中。extern宣告不會引起記憶體被分配,它可以在同一檔案中或同一程式的不同檔案中出現多次。關鍵字extern也可以在函式宣告中指定,唯一的影響是將該宣告為隱式屬性,在其它地方定義,變為顯示的。extern關鍵字告訴編譯器,”這個變數可能定義在這個模組或其它模組中”,一個extern宣告並沒有生成資料,它僅表明這個資料是共享的。這個變數必須是在別處定義過的,而且它只能定義一次。

以下是測試程式碼:

extern.hpp:

#ifndef FBC_CPPBASE_TEST_EXTERN_HPP_
#define FBC_CPPBASE_TEST_EXTERN_HPP_

namespace extern_ {

extern const int bufsize; // 與.cpp中定義的bufsize是同一個

int test_extern_1();
int test_extern_2();

} // namespace extern_

#endif // FBC_CPPBASE_TEST_EXTERN_HPP_
extern.cpp:

#include "extern.hpp"
#include <iostream>

extern std::string extern_variable_blog_addr;
extern std::string extern_variable_github_addr;
extern int extern_function_add(int a, int b);
extern const std::string extern_function_name();

namespace extern_ {

namespace {
	int fcn() { return 0; }
}

extern double pi = 3.1415; // 定義

/* reference: C++ Primer(Fifth 中文版) pages 63
某些時候有這樣一種const變數,它的初始值不是一個常量表示式,但又確實有必要在檔案間共享。
這種情況下,我們不希望編譯器為每個檔案分別生成獨立的變數。相反,我們想讓這類const物件
像其它(非常量)物件一樣工作,也就是說,只在一個檔案中定義const,而在其它多個檔案中宣告
並使用它。解決的辦法是,對於const變數不管是宣告還是定義都新增extern關鍵字,這樣只需定義一次就可以了
*/
extern const int bufsize = fcn();

int test_extern_1()
{
	extern int i; // 宣告i而非定義i
	int j; // 宣告並定義j
	//extern double pi = 3.1415; // 定義,在函式體內部,如果試圖初始化一個由extern關鍵字標記的變數,將引發錯誤

	return 0;
}

int test_extern_2()
{
	fprintf(stdout, "blob addr: %s\n", extern_variable_blog_addr.c_str());
	fprintf(stdout, "github addr: %s\n", extern_variable_github_addr.c_str());

	fprintf(stdout, "a + b = %d\n", extern_function_add(2, 3));

	fprintf(stdout, "name: %s\n", extern_function_name().c_str());

	return 0;
}

} // namespace extern_
extern2.hpp:

#ifndef FBC_CPPBASE_TEST_EXTERN2_HPP_
#define FBC_CPPBASE_TEST_EXTERN2_HPP_

#include <string>

extern std::string extern_variable_blog_addr;
extern std::string extern_variable_github_addr;
extern int extern_function_add(int a, int b);
extern const std::string extern_function_name();

#endif // FBC_CPPBASE_TEST_EXTERN2_HPP_
extern2.cpp:

#include "extern2.hpp"

std::string extern_variable_blog_addr{ "http://blog.csdn.net/fengbingchun" };
std::string extern_variable_github_addr{ "https://github.com/fengbingchun" };

int extern_function_add(int a, int b)
{
	return (a + b);
}

extern const std::string extern_function_name()
{
	return "C++";
}

GitHub: https://github.com/fengbingchun/Messy_Test 

相關文章