Saves space by not creating out-of-line copies of inline functions controlled by #pragma statements. Using this option will generate linker errors if the such functions are not inlined everywhere they are called
(GCC 4.02或以后)使g++不導出ELF(Executable and Linking Format,Linux和Solaris等系統上默認的2進制文件格式)中用hidden標識的object模塊內或庫內的符號。該選項能減少目標文件大小,加快符號表的查找,從而改善運行性能。但是,該選項也會因為不同的visibility等級而導致模塊間拋出異常發生問題,詳見后面的““Visibility Attributes and Pragmas for GCC C++ Libraries”一節。如果沒有使用該選項,那么默認的visibility值是default,即導出所有目標文件和庫里的符號。
當非模板的友元函數定義在模板里時不給出警告。In the C++ language template specification, a friend must declare or define a nontemplate function if the name of the friend is an unqualified identifier.
ABI Differences in g++ Versions C++ ABI是一套API標準,定義了C++庫提供的數據類型、類、方法、頭文件等的接口和規范。對庫和目標文件來說,物理組織、參數傳遞方式和命名方式是很重要的,所以需要一個統一的接口,使編譯出來的C++程序與提供的庫的接口一致。這種一致性對語言特有的一些屬性更加重要,比如拋出異常和捕捉異常的時候。 從版本3開始的GNU C++編譯器,都遵循一個工業標準的C++ ABI規范,定義在http://www.codesourcery.com/cxx-abi/abi.html。雖然這個規范是為64位Itanium定制的,但是它適用于任何平臺,并且已經作為GNU/Linux和BSD系統的C++ ABI的實現。 版本3.4以前的g++使用ABI版本1,之后使用ABI版本2。不同ABI版本之間的程序和庫不能混用。如果你不確定自己g++的ABI版本,可以用g++ --version命令檢查g++的版本,或用一個偽編譯命令顯示ABI標識符,命令行如下: g++ -E -dM - < /dev/null | awk '/GXX_ABI/ {print $3}' 如果顯示102,那么就是版本1;如果顯示1002,就是版本2。如果你必須用到以前版本ABI的庫,那么給g++加上選項-fabi-version=n,其中n就是你要兼容的ABI版本。這樣做只能算作權宜之計,把所有舊的代碼和庫更新到當前版本才是最佳解決方案。 GNU C++ Implementation Details and Extensions 本文雖然不討論怎樣寫好C++程序,但是當你用GCC的C++編譯器編譯你的C++程序的時候,你可以從GCC的擴展中得到許多好處,包括編譯器自身的優勢和g++使用的標準C++庫libstdc++的優勢。本節提煉出最為重要的一些擴展特性,并討論它們在C++規范和編譯器行為方面的一些差異。 Attribute Definitions Specific to g++ 作為對visibility屬性(詳見于“Visibility Attributes and Pragmas for GCC C++ Libraries”)的補充,g++提供了2個額外的屬性,即init_priority(priority)和java_interface屬性。 The init_priority Attribute 該屬性允許用戶控制某個名字空間里的對象的初始化順序。通常,對象的初始化順序是它們在某個代碼單元里的定義順序。init_priority只有一個整型參數,值為101到65535,越小表示優先級越大。比如,在下面的偽碼里,類MyClass將比類YourClass先初始化: class MyClass { … }; class YourClass { __attribute__ ((visibility("default"))) void MyMethod(); … }; 要改變它們的初始化順序,你可以把代碼改成下面這樣: class MyClass { __attribute__ ((init_priority(65535))); … }; class YourClass { __attribute__ ((init_priority(101))); … }; 你只需要注意所使用的優先級數值的順序,具體使用了哪個數值則無所謂(即只要MyClass的優先級數值比YourClass大就行了,是不是65535和101則無所謂)。 The java_interface Attribute 該屬性通知g++某個類是一個Java接口類,并只能在標識了extern “Java”的模塊內使用。調用這個類的函數使用的是GCC Java編譯器的接口表機制(interface table mechanism),而不是通常的C++虛函數表機制(virtual function table mechanism)。 提示記住,Java的運行時環境需要更多的初始化工作。當你混合使用C++和Java代碼時,最好用Java寫主程序,這樣能保證調用Java函數前初始化工作已經做足了。C++ Template Instantiation in g++ 模板是C++最有用和最有趣的特性之一,能減少重復代碼,提高復用率,簡化調試和代碼維護工作。模板也有利于編譯時的類型檢查,比如,使用了模板就不用再傳遞void指針,因為你可以把模板參數實例化成任何需要的類型。 g++通過增加3個功能擴展了標準的ISO模板定義: l?支持使用extern關鍵詞對實例化類型進行前置申明; l?The ability to instantiate the support data required by the compiler for a named template class without actually instantiating it by using the inline keyword l?The ability to only instantiate the static data members of a class without instantiating support data or member functions by using the static keyword 基本上,GCC的g++編譯器支持Borland和Cfront(AT&T)兩種模板特性。要支持Borland的模板實例化和使用特性,g++使用-frepo選項允許預處理器在處理每個翻譯單元(源代碼文件)時進行模板實例化,并把信息存在.rpo文件里。這些文件被后面的編譯過程使用,并由鏈接器最后合并成單個編譯單元。要支持Cfront特性,g++內置了一個模板實例化庫并在鏈接的時候合并到代碼里。Cfront要求使用模板的代碼要么進行顯式實例化,要么包含定義模板的申明文件。你可以把顯式實例化放在代碼的任何地方,或一個包含的頭文件里。對于后者,你可能要去掉-fno-implicit-templates選項,這樣你只得到了顯式實例化的那些實例。 Function Name Identifiers in C++ and C GCC編譯器預定義了2個標識符存儲當前函數的標識。__FUNCTION__標識符只存儲函數名字,__PRETTY_FUNCTION__則存儲函數的全稱。在C程序里,這2種名字是一樣的,但是在C++程序里它們有區別。下面的程序展示了這種區別: #include <iostream> using namespace std; class c { public: void method_a(void) { cout<<"Function "<<__FUNCTION__<<" in "<<__FILE__<< endl; cout<<"Pretty Function "<<__PRETTY_FUNCTION__<<" in " << __FILE__ << endl; } }; int main(void) { c C; C.method_a(); return 0; } 運行的輸出是: $ ./a.out Function method_a in FUNCTION_example.cc Pretty Function void c::method_a() in FUNCTION_example.cc 在C++里,__FUNCTION__和__PRETTY_FUNCTION__是變量,而不是宏定義,所以#ifdef __FUNCTION__是沒有意義的。 注意如果你的GCC是3.2版本或更高,那么__FUNCTION__和__PRETTY_FUNCTION__的行為就和C99定義的__func__變量是一樣的。早于3.2版本的GCC編譯器把__FUNCTION__和__PRETTY_FUNCTION__定義成字符串,所以它們可以和其他字符串進行串接操作。Minimum and Maximum Value Operators g++編譯器加入了<?和>?操作符,分別表示2個數值中較小的和較大的那個。比如,下面的代碼把10賦給min變量: min = 10 <? 15; 而下面的代碼把15賦給max: max = 10 >? 15; 提示既然這些操作符是語言提供的,那么它們也能對任何類或enum類型進行重載。Using Java Exception Handling in C++ Applications Java和C++的異常處理模型是不同的,雖然g++能猜測C++代碼何時使用了Java異常,你最好還是明確標識出這種情況,避免鏈接錯誤。要告訴g++一塊代碼可能使用Java異常,把下面的代碼放在該翻譯單元中任何catch和throw代碼之前: #pragma GCC java_exceptions 你不能在一個翻譯單元里同時使用Java和C++異常。 Visibility Attributes and Pragmas for GCC C++ Libraries 寫C++庫的時候一個普遍的問題就是可見的ELF符號太多了,其實許多符號都不能被外部使用,也不用對外公開。GCC版本4.02及更高提供了-fvisibility=value選項和相關的內置屬性,使你可以控制這種行為,使用的方式和微軟C++編譯器提供的__declspec(dllexport)方式相似。新的-fhidden選項有2個可選值:default,導出目標文件的所有符號(這也是默認的行為);hidden,不導出當前目標模塊的符號。還可以在函數或類前加如下代碼來進行設置:__attribute__ ((visibility("default")))和__attribute__ ((visibility("hidden")))。 默認情況下,ELF導出全部符號。要隱藏特定目標文件的符號,需要在編譯該文件的時候加上-fvisibility=hidden選項。這將導致makefile的復雜性大大增加,因為你要么需要手動設置每個文件的編譯選項,要么改變全局編譯選項導致任何符號都不能導出。這在類庫正常拋出異常或者調試某些變量的時候實在是個災難。 讓指定的符號可見的好方式是聯合使用代碼屬性設置和編譯選項-fvisibility=hidden。如果要導出某個符號,先在它們的定義前加上__attribute__((visibility("default"))),比如下面這樣: class MyClass { int i; __attribute__ ((visibility("default"))) void MyMethod(); … }; 然后給makefile增加-fvisibility=hidden的編譯選項,這樣所有其他的符號就被隱藏了。另一個稍微好點的方法是定義一個宏,并放到所有你不想導出的符號定義前面,然后使用默認的導出所有符號,如下所示: #define LOCAL __attribute__ ((visibility("hidden"))) class MyClass { int i; LOCAL void MyMethod(); … }; 編譯時不使用-fvisibility=value選項,這樣除了MyMethod被隱藏,其他符號都被導出。 還有一種控制可見屬性的pragma語法現在還能使用,不過將來可能要去掉,如下面這樣: extern void foo(int); #pragma GCC visibility push(hidden) extern void bar(int); #pragma GCC visibility pop 符號foo會被導出,但是bar則不會。這種方式雖然很簡單方便,但是建議你還是使用visibility和__attribute__。