C++ 允許同一范圍內具有相同名稱的多個函數的規范。這些函數稱為重載函數,“重載”中對其進行了詳細介紹。利用重載函數,程序員可以根據參數的類型和數量為函數提供不同的語義。
例如,采用字符串(或 char *)參數的 print 函數執行的任務與采用“雙精度”類型的參數的函數執行的任務截然不同。重載允許通用命名并使程序員無需創建名稱,例如 print_sz 或 print_d。下表顯示了 C++ 使用函數聲明的哪些部分來區分同一范圍內具有相同名稱的函數組。
重載注意事項
| 函數聲明元素 | 是否用于重載? |
|---|---|
| 函數返回類型 | No |
| 參數的數量 | 是 |
| 參數的類型 | 是 |
| 省略號存在或缺失 | 是 |
| typedef 名稱的使用 | 否 |
| 未指定的數組邊界 | 否 |
| const 或 volatile(見下文) | 是 |
// function_overloading.cpp// compile with: /EHsc#include <iostream>#include <math.h>// Prototype three print functions.int print( char *s ); // Print a string.int print( double dvalue ); // Print a double.int print( double dvalue, int prec ); // Print a double with a// given precision.using namespace std;int main( int argc, char *argv[] ){const double d = 893094.2987;if( argc < 2 ) {// These calls to print invoke print( char *s ).print( "This program requires one argument." );print( "The argument specifies the number of" );print( "digits precision for the second number" );print( "printed." );exit(0); }// Invoke print( double dvalue ).print( d );// Invoke print( double dvalue, int prec ).print( d, atoi( argv[1] ) );}// Print a string.int print( char *s ){cout << s << endl;return cout.good();}// Print a double in default precision.int print( double dvalue ){cout << dvalue << endl;return cout.good();}// Print a double in specified precision.// Positive numbers for precision indicate how many digits// precision after the decimal point to show. Negative// numbers for precision indicate where to round the number// to the left of the decimal point.int print( double dvalue, int prec ){// Use table-lookup for rounding/truncation.static const double rgPow10[] = { 10E-7, 10E-6, 10E-5, 10E-4, 10E-3, 10E-2, 10E-1, 10E0,10E1, 10E2, 10E3, 10E4, 10E5, 10E6 };const int iPowZero = 6;// If precision out of range, just print the number.if( prec < -6 || prec > 7 )return print( dvalue );// Scale, truncate, then rescale.dvalue = floor( dvalue / rgPow10[iPowZero - prec] ) *rgPow10[iPowZero - prec];cout << dvalue << endl;return cout.good();}前面的代碼演示了文件范圍內的 print 函數重載。
默認參數不被視為函數類型的一部分。因此,它不用于選擇重載函數。僅在默認參數上存在差異的兩個函數被視為多個定義而不是重載函數。
不能為重載運算符提供默認參數。
參數匹配
選擇重載函數以實現當前范圍內的函數聲明與函數調用中提供的參數的最佳匹配。如果找到合適的函數,則調用該函數。此上下文中的“Suitable”具有下列含義之一:
編譯器為每個參數創建一組候選函數。候選函數是這樣一種函數,其中的實參可以轉換為形參的類型。
為每個參數生成一組“最佳匹配函數”,并且所選函數是所有集的交集。如果交集包含多個函數,則重載是不明確的并會生成錯誤。對于至少一個參數而言,最終選擇的函數始終是比組中的所有其他函數更好的匹配項。如果不是這樣(如果沒有清晰的勝者),則函數調用會生成錯誤。
考慮下面的聲明(針對下面的討論中的標識,將函數標記為 Variant 1、Variant 2 和 Variant 3):
Fraction &Add( Fraction &f, long l ); // Variant 1Fraction &Add( long l, Fraction &f ); // Variant 2Fraction &Add( Fraction &f, Fraction &f ); // Variant 3Fraction F1, F2;
請考慮下列語句:
F1 = Add( F2, 23 );
前面的語句生成兩個集:
| 集 1:其第一個參數的類型為 Fraction 的候選函數 | 集 2:其第二個參數可轉換為類型 int 的候選函數 |
|---|---|
| Variant 1 | Variant 1(可使用標準轉換將 int 轉換為 long) |
| Variant 3 |
F1 = Add( 3, 6 );
前面的函數調用生成以下集:
| 集 1:其第一個參數的類型為 int 的候選函數 | 集 2:其第二個參數的類型為 int 的候選函數 |
|---|---|
| Variant 2(可使用標準轉換將 int 轉換為 long) | Variant 1(可使用標準轉換將 int 轉換為 long) |
// argument_type_differences.cpp// compile with: /EHsc /W3// C4521 expected#include <iostream>using namespace std;class Over {public: Over() { cout << "Over default constructor/n"; } Over( Over &o ) { cout << "Over&/n"; } Over( const Over &co ) { cout << "const Over&/n"; } Over( volatile Over &vo ) { cout << "volatile Over&/n"; }};int main() { Over o1; // Calls default constructor. Over o2( o1 ); // Calls Over( Over& ). const Over o3; // Calls default constructor. Over o4( o3 ); // Calls Over( const Over& ). volatile Over o5; // Calls default constructor. Over o6( o5 ); // Calls Over( volatile Over& ).}輸出
Over default constructorOver&Over default constructorconst Over&Over default constructorvolatile Over&
指向 const 和 volatile 對象的指針也被認為和指向基類型的指針(以重載為目的)不同。
參數匹配和轉換
當編譯器嘗試根據函數聲明中的參數匹配實際參數時,如果未找到任何確切匹配項,它可以提供標準轉換或用戶定義的轉換來獲取正確類型。轉換的應用程序受這些規則的限制:
不考慮包含多個用戶定義的轉換的轉換序列。
不考慮可通過刪除中間轉換來縮短的轉換序列。
最終的轉換序列(如果有)稱為最佳匹配序列。可通過多種方式使用標準轉換將類型 int 的對象轉換為類型unsigned long 的對象(如標準轉換中所述):
第一個序列(盡管它實現了所需目標)不是最佳匹配序列 - 存在一個較短的序列。
下表顯示了一組稱為常用轉換的轉換,這些轉換對確定哪個序列是最佳匹配項有一定的限制。該表后面的列表中討論了常用轉換影響序列選擇的實例。
常用轉換
| 從類型轉換 | 轉換為類型 |
|---|---|
| type-name | type-name & |
| type-name & | type-name |
| type-name [ ] | type-name* |
| type-name ( argument-list ) | ( *type-name ) ( argument-list ) |
| type-name | const type-name |
| type-name | volatile type-name |
| type-name* | const type-name* |
| type-name* | volatile type-name* |
使用提升的匹配。未歸類為僅包含整型提升、從 float 到 double 的轉換以及常用轉換的完全匹配的任何序列將被歸類為使用提升的匹配。盡管比不上完全匹配,但使用提升的匹配仍優于使用標準轉換的匹配。
使用標準轉換的匹配。未歸類為完全匹配或僅包含標準轉換和常用轉換的使用提升的匹配的序列將歸類為使用標準轉換的匹配。在此類別中,以下規則將適用:
從指向派生類的指針到指向直接或間接基類的指針的轉換優于到 void * 或 const void * 的轉換。
從指向派生類的指針到指向基類的指針的轉換會產生一個到直接基類的更好匹配。假定類層次結構如下圖所示。

演示首選轉換的關系圖
從 D* 類型到 C* 類型的轉換優于從 D* 類型到 B* 類型的轉換。同樣,從 D* 類型到 B* 類型的轉換優于從 D* 類型到 A* 類型的轉換。
此同一規則適用于引用轉換。從 D& 類型到 C& 類型的轉換優于從 D& 類型到 B& 類型的轉換等。
此同一規則適用于指向成員的指針轉換。從 T D::* 類型到 T C::* 類型的轉換優于從 T D::* 類型到 T B::* 類型的轉換等(其中,T 是該成員的類型)。
前面的規則僅沿派生的給定路徑應用。考慮下圖中顯示的關系圖。

演示首選轉換的多繼承關系圖
從 C* 類型到 B* 類型的轉換優于從 C* 類型到 A* 類型的轉換。原因是它們位于同一個路徑上,且 B* 更為接近。但是,從 C* 類型到 D* 類型的轉換不優于到 A* 類型的轉換;沒有首選項,因為這些轉換遵循不同的路徑。
使用用戶定義的轉換的匹配。此序列不能歸類為完全匹配、使用提升的匹配或使用標準轉換的匹配。序列必須僅包含用戶定義的轉換、標準轉換或要歸類為使用用戶定義的轉換的匹配的常用轉換。使用用戶定義的轉換的匹配被認為優于使用省略號的匹配,但比不上使用標準轉換的匹配。
使用省略號的匹配。與聲明中的省略號匹配的任何序列將歸類為使用省略號的匹配。這被視為最弱匹配。
如果內置提升或轉換不存在,則用戶定義的轉換將適用。基于將匹配的參數的類型選擇這些轉換。考慮下列代碼:
// argument_matching1.cppclass UDC{public: operator int() { return 0; } operator long();};void Print( int i ){};UDC udc;int main(){ Print( udc );}類 UDC 的可用的用戶定義的轉換來自 int 類型和 long 類型。因此,編譯器會考慮針對將匹配的對象類型的轉換:UDC。到 int 的轉換已存在且已被選中。
在匹配參數的過程中,標準轉換可應用于參數和用戶定義的轉換的結果。因此,下面的代碼將適用:
void LogToFile( long l );...UDC udc;LogToFile( udc );
在前面的示例中,將調用用戶定義的轉換 operator long 以將 udc 轉換為類型 long。如果未定義到 long 類型的用戶定義的轉換,則按如下所示繼續轉換:使用用戶定義的轉換將 UDC 類型轉換為 int 類型。將應用從 int 類型到 long 類型的標準轉換以匹配聲明中的參數。
如果需要任何用戶定義的轉換來匹配參數,則在計算最佳匹配時不會使用標準轉換。即使多個候選函數需要用戶定義的轉換也是如此;在這種情況下,這些函數被認為是相等的。例如:
// argument_matching2.cpp// C2668 expectedclass UDC1{public: UDC1( int ); // User-defined conversion from int.};class UDC2{public: UDC2( long ); // User-defined conversion from long.};void Func( UDC1 );void Func( UDC2 );int main(){ Func( 1 );}Func 的兩個版本都需要用戶定義的轉換以將類型 int 轉換為類類型參數。可能的轉換包括:
即使其中的第二個轉換需要標準轉換以及用戶定義的轉換,這兩個轉換仍被視為相等。
注意
用戶定義的轉換被認為是通過構造函數的轉換或通過初始化的轉換(轉換函數)。在考慮最佳匹配時,兩個方法被認為是相等的。
參數匹配和 this 指針
處理類成員函數的方式各不相同,具體取決于它們是否已被聲明為 static。由于非靜態函數具有提供 this 指針的隱式參數,因此將非靜態函數視為比靜態函數多一個參數;否則,將以相同的方式聲明這些函數。
這些非靜態成員函數要求隱含的 this 指針與通過其調用函數的對象類型匹配,或者對于重載運算符,它們要求第一個參數與該運算符應用于的對象匹配。
與重載函數中的其他參數不同,當嘗試匹配 this 指針參數時,不會引入臨時對象,且不會嘗試轉換。
當
主站蜘蛛池模板:
新干县|
安化县|
隆子县|
开原市|
隆子县|
潜江市|
昌江|
金川县|
天峻县|
通河县|
洪泽县|
永定县|
潜山县|
霞浦县|
高阳县|
河北省|
建平县|
九龙县|
永年县|
昭平县|
黄骅市|
梁平县|
南和县|
长兴县|
平武县|
托里县|
哈尔滨市|
衡南县|
邵阳县|
遂平县|
宁远县|
江安县|
富宁县|
米脂县|
册亨县|
黔南|
盱眙县|
文成县|
寻甸|
富蕴县|
黄平县|