『壹』 重載的編程語言中的重載
編程中重載的定義:函數名相同,函數的參數列表不同(包括參數個數和參數類型),至於返回類型可同可不同。
重載是可使函數、運算符等處理不同類型數據或接受不同個數的參數的一種方法,關於重載一詞在詞義上有兩種不同的說法: 重載是一種多態(如C++,java),有四種形式的多態:
1.虛函數多態
2模板多態
3重載
4轉換
所謂的動態和靜態區分是另一種基於綁定時間的多態分類,嚴格來說,重載是編譯時多態,即靜態多態,根據不同類型函數編譯時會產生不同的名字如int_foo和char_foo等等,以此來區別調用。故重載仍符合多態定義——通過單一標識支持不同特定行為的能力,只是重載屬於靜態多態,而不是通過繼承和虛函數實現的動態多態。 重載(overloaded)和多態無關,真正和多態相關的是覆蓋(inheritance)。
當派生類重新定義了基類的虛擬方法後,基類根據賦給它的不同的派生類引用,動態地調用屬於派生類的對應方法,這樣的方法調用在編譯期間是無法確定的。因此,這樣的方法地址是在運行期綁定的(動態綁定)。
重載只是一種語言特性,是一種語法規則,與多態無關,與面向對象也無關。
不過針對所謂的第二種重載,有一個專門的名詞--重寫或重定義。重載與重寫的區別就在於是否覆蓋,重寫一般多發生在不同的類且存在繼承關系之間,而重載多是在一個類里或者一塊代碼段里。
特點:
由於重載可以在同一個類中定義功能類似的函數,這給程序員管理類的相似函數提供了極大的方便。例如,在一個定義圓的類中,需要設定圓心和半徑來確定一個圓對象,程序員不需要設定setRadius(float r)和SetPoint(float x,float y)兩個不同名函數,而只需要設定一個CSetCicle函數名就夠了。在這個簡單的例子中重載並沒有明顯的優勢,可是當一個類中相似功能函數有幾十、上百個的時候,重載的優勢就顯現出來了,這時程序員不需要去記這么繁多的函數名,可以把更多的精力放在程序本身上。重載的方法只屬於子類。
函數:
1.函數名必須相同,返回值可以相同,也可以不同,但是特徵標必須不同。是函數名來確定函數的不同,是特徵標是函數可以重載。編譯器首先選在函數名,然後再根據特徵標在眾多重載的函數中找到合適的。
2.匹配函數時,編譯器將不區分類型引用和類型本身,也不區分const和非const變數。(小註:因為這些在定義和聲明時可能不同,但是在調用時都是一樣的,編譯器將無法區分)。但是值得注意的是,形參與const形參的等價性僅適於非引用形參。有const引用形參的函數與有非const引用形參的函數是不同的。類似的,如果函數帶有指向const類型的指針形參,則與帶有指向相同類型的非const對象的指針形參的函數不相同。
3.名稱修飾(name decoration)。編譯器將根據原型中指定的形參對每個函數名進行加密。
重定義:
被重載的函數有不同版本,這些函數地位是一樣的,可以根據特徵標的不同選擇不同的函數。被重定義的函數也有不同的版本,但是你不能隨意選擇,你只能選擇最新的版本,被重定義多發生在類之間的繼承里。
4.函數會有那麼多版本,那麼編譯將選哪一個呢。當然,理想情況是,實參與形參的數據類型完全匹配,但是當不完全匹配時會怎樣呢?這就要牽扯到c++里復雜的類型轉換了。
在重載及函數模板重載里,編譯器選擇函數,要經過以下三步,這個過程稱為重載解析。
第一步:創建候選函數列表,其中包含有與被調函數名稱相同的函數與模板函數。
第二步:使用候選函數列表創建可行函數列表。這些都是參數數目正確的函數。
第三步:確定是否有最佳可行的函數。如果有,則使用。
確定最佳函數,只考慮其特徵標,而不考慮返回類型(也無從考慮,但是要是硬想辦法的話,也有,不過沒有必要為了不必要的性能而浪費資源)。確定最佳函數,匹配特徵標要依次經過以下判斷:(1)完全匹配(常規函數優於模板;允許無關緊要的轉換)(2)提升匹配(如char和short自動轉換為int)(3)標准轉換(int轉換為char,long轉換為double)(4)用戶自定義的轉換(如類聲明中定義的轉換函數)。
完全允許無關緊要的轉換,這些轉換包括引用,指針與實體之間,數組與指針之間,函數與函數指針之間,const與非const等等。
其次還要注意匹配的優先順序。1,指向非const數據的指針和引用優先於const的指針和引用參數匹配(這種優先順序只有當指針或引用出現時產生)。2,非模板函數,優於模板函數,顯示具體化的模板將優於隱式具體化的模板,總之較具體的優先(注意,具體並不是由於顯隱決定的,術語「最具體」是指編譯器推斷使用哪種類型時執行的轉換最少)。 通常,派生類繼承基類的方法,因此,在調用對象繼承方法的時候,調用和執行的是基類的實現.但是,有時需要對派生類中的繼承方法有不同的實現.
例如,假設動物類存在跑的方法,從中派生出馬和狗,馬和狗的跑得形態是各不相同的,因此同樣方法需要兩種不同的實現,這就需要重新編寫基類中的方法.
重寫基類方法就是修改它的實現或者說在派生類中重新編寫 //java代碼//方法重寫publicclassFather{publicvoidovel(inti){/*dosomething...*/}publicStringovef(){/*dosomething...*/return***;}}publicclassSonextendsFather{publicvoidovel(inti){/*doothersomething...*/}publicStringovef(){/*doothersomething...*/returnXXX;}}
『貳』 JAVA 的重載是運行時決定還是編譯的時候決
重載屬於編譯時多態。運行時會根據參數的個數和類型調用相應的方法。
『叄』 C++函數重載的編譯後的一個奇怪的問題
是的,對於編譯器來說,名字是不一樣而且唯一的,編譯器會對函數名「修飾」。
C++ 編譯器的函數名修飾規則
函數名字修飾(Decorated Name)方式
函數的名字修飾(Decorated Name)就是編譯器在編譯期間創建的一個字元串,用來指明函數的定義或原型。LINK程序或其他工具有時需要指定函數的名字修飾來定位函數的正確位置。多數情況下程序員並不需要知道函數的名字修飾,LINK程序或其他工具會自動區分他們。當然,在某些情況下需要指定函數的名字修飾,例如在C++程序中,為了讓LINK程序或其他工具能夠匹配到正確的函數名字,就必須為重載函數和一些特殊的函數(如構造函數和析構函數)指定名字裝飾。另一種需要指定函數的名字修飾的情況是在匯編程序中調用C或C++的函數。如果函數名字,調用約定,返回值類型或函數參數有任何改變,原來的名字修飾就不再有效,必須指定新的名字修飾。C和C++程序的函數在內部使用不同的名字修飾方式,下面將分別介紹這兩種方式。
1.C編譯器的函數名修飾規則
對於__stdcall調用約定,編譯器和鏈接器會在輸出函數名前加上一個下劃線前綴,函數名後面加上一個「@」符號和其參數的位元組數,例如_functionname@number。__cdecl調用約定僅在輸出函數名前加上一個下劃線前綴,例如_functionname。__fastcall調用約定在輸出函數名前加上一個「@」符號,後面也是一個「@」符號和其參數的位元組數,例如@functionname@number
2.C++編譯器的函數名修飾規則
C++的函數名修飾規則有些復雜,但是信息更充分,通過分析修飾名不僅能夠知道函數的調用方式,返回值類型,參數個數甚至參數類型。不管__cdecl,__fastcall還是__stdcall調用方式,函數修飾都是以一個「?」開始,後面緊跟函數的名字,再後面是參數表的開始標識和按照參數類型代號拼出的參數表。對於__stdcall方式,參數表的開始標識是「@@YG」,對於__cdecl方式則是「@@YA」,對於__fastcall方式則是「@@YI」。參數表的拼寫代號如下所示:
X--void
D--char
E--unsigned char
F--short
H--int
I--unsigned int
J--long
K--unsigned long(DWORD)
M--float
N--double
_N--bool
U--struct
....
指針的方式有些特別,用PA表示指針,用PB表示const類型的指針。後面的代號表明指針類型,如果相同類型的指針連續出現,以「0」代替,一個「0」代表一次重復。U表示結構類型,通常後跟結構體的類型名,用「@@」表示結構類型名的結束。函數的返回值不作特殊處理,它的描述方式和函數參數一樣,緊跟著參數表的開始標志,也就是說,函數參數表的第一項實際上是表示函數的返回值類型。參數表後以「@Z」標識整個名字的結束,如果該函數無參數,則以「Z」標識結束。下面舉兩個例子,假如有以下函數聲明:
int Function1 (char *var1,unsigned long);
其函數修飾名為「?Function1@@YGHPADK@Z」,而對於函數聲明:
void Function2();
其函數修飾名則為「?Function2@@YGXXZ」 。
對於C++的類成員函數(其調用方式是thiscall),函數的名字修飾與非成員的C++函數稍有不同,首先就是在函數名字和參數表之間插入以「@」字元引導的類名;其次是參數表的開始標識不同,公有(public)成員函數的標識是「@@QAE」,保護(protected)成員函數的標識是「@@IAE」,私有(private)成員函數的標識是「@@AAE」,如果函數聲明使用了const關鍵字,則相應的標識應分別為「@@QBE」,「@@IBE」和「@@ABE」。如果參數類型是類實例的引用,則使用「AAV1」,對於const類型的引用,則使用「ABV1」。下面就以類CTest為例說明C++成員函數的名字修飾規則:
classCTest
{
......
private:
voidFunction(int);
protected:
voidCopyInfo(constCTest&src);
public:
longDrawText(HDChdc,longpos,constTCHAR*text,RGBQUADcolor,BYTEbUnder,boolbSet);
longInsightClass(DWORDdwClass)const;
......
};
對於成員函數Function,其函數修飾名為「?Function@CTest@@AAEXH@Z」,字元串「@@AAE」表示這是一個私有函數。成員函數CopyInfo只有一個參數,是對類CTest的const引用參數,其函數修飾名為「?CopyInfo@CTest@@IAEXABV1@@Z」。DrawText是一個比較復雜的函數聲明,不僅有字元串參數,還有結構體參數和HDC句柄參數,需要指出的是HDC實際上是一個HDC__結構類型的指針,這個參數的表示就是「PAUHDC__@@」,其完整的函數修飾名為「?DrawText@CTest@@QAEJPAUHDC__@@JPBDUtagRGBQUAD@@E_N@Z」。InsightClass是一個共有的const函數,它的成員函數標識是「@@QBE」,完整的修飾名就是「?InsightClass@CTest@@QBEJK@Z」。
『肆』 c++6.0編譯運算符的重載時 結果不正確的問題
char a;
char b;
::strcpy(&this->a,&s.a);
::strcpy(&this->b,&s.b);
::strcpy(&m.a,&this->a);
::strcpy(&m.b,&this->b);
上面是錯的地方,你要仔細理解一下 char 和char *,或者char 數組的區別
『伍』 函數重載相同函數名編譯時函數名不同是啥意思
意思是說,我們看到的函數重載名稱相同,但編譯的時候系統會自動給他們另外起一個唯一的名稱,各不相同,這里你不用糾結
『陸』 Java里,重載的方法為何是靜態編譯,而沒有重載的方法卻是動態編譯這么設計有什麼原因嗎
java允許在一個類中,多個方法擁有相同的名字,但在名字相同的同時,必須有不同的參數,這就是重載,編譯器會根據實際情況挑選出正確的方法,如果編譯器找不到匹配的參數或者找出多個可能的匹配就會產生編譯時錯誤,這個過程被稱為重載的解析
1 普通方法的重載
普通方法的重載是Java實現多態技術的重要手段,為編程帶來了很多便利
當方法同名時,為了讓編譯器區別他們,至少需要下面之一不同
1 參數個數不同
2 對應位置上的參數類型不同
不允許參數完全相同而只是返回值不同的情況出現。無法進行編譯,程序在eclips中顯示錯誤
2 構造方法的重載
見文章構造方法的繼承
重載的解析
當類的設計者提供了重載方法之後,類的使用者在使用這些方法時編譯器需要確定調用哪一個方法,確定的唯一依據是參數列表,確定的過程被稱為重載的解析。
以下舉些例子說明:
show(int a ,int b,int c) //1
show(int a ,int b,double c) //2
show(int a ,double b,double c)//3
show(double a,double b,int c) //4
下面是調用
show(1,2,3);//1,2,3,4都是可行方法所有參數完全匹配1
show(1.0,2.0,3.0);//沒有一個可行方法
show(1.0,2,3);//4是最佳可行方法
show(1,2.0,3);//3,4都是可行方法,沒有最佳可行方法,報錯
重載和覆蓋都是多態的表現,他們在某些地方很相似,很容易引起初學者的疑惑,這里將它們之間的區別總結如下
1 重載和覆蓋的方法名稱都相同,但重載要求參數列表不同,而覆蓋要求參數列表完全相同。
2 重載對於方法前面的修飾符沒有限制,而覆蓋則對這些修飾符的使用有限制
3 重載時編譯器在編譯期間就可以確定調用那一個方法,而覆蓋則有可能在運行期間才能確定。
『柒』 重載>>,編譯報錯(重載其他運算符都沒問題,就是這個有問題)
為什麼:
頭文件里的:
friend std::ostream& operator >>(std::ostream &is,ccomplex &a);
cpp文件里的:
std::istream& operator >>(std::istream &is,ccomplex &a)
為什麼函數簽名不一樣呢?
『捌』 C++多態性。 基類的虛函數和重載函數的區別,是不是一個是在運行時發生,一個是在編譯時發生
是的,函數的重載是在編譯的時候發生,而虛函數是在運行的時候確定。其實有時候稱函數的重載為靜態多態性,而虛函數為動態多態性
『玖』 VC++運算符重載時編譯問題
int operator +(int g,a &p) //這里應該也用&,因為前面用了,見friend int operator +(int ,a &);
『拾』 c++函數重載"<<"為什麼編譯通不過
把iostream、ostream、using namespace std三行去掉,換成 iostream.h
我記得這是vc6的bug,它們和友元會沖突。
本來昨晚就可以答的,結果由於網路知道故障而回答不了