6.4 函數的重載
- 函數的名字相同但是形參的列表不同,將其稱之為重載函數
void print(const char *cp);
void print(const int *beg,const int * end);
void print(const int ia[],size_t size);
- 形如上面所展現的這樣,當調用這些函數的時候,編譯器會根據傳遞的實參類型推斷出想要的是哪個函數。
- 重載函數在一定程度上減輕了程序員對于起名字和記名字的負擔。
- mian函數不可以重載
定義承載函數
- 比如數據庫的應用場景中,定義一個查詢的函數,對于手機號、名字、賬戶號碼分別進行查詢。他們雖然查詢的東西不一樣,但是函數的名字是一樣的,編譯器會根據傳入的實參的類型決定調用哪一個函數。
- 對于重載函數而言,應該在形參的數量、類型有所不同。
- 不允許兩個函數除了返回的類型不同,形參列表都相同。那么第二個聲明是錯誤的。
判斷兩個形參的類型是否不同
- 有時候兩個形參列表看起來不一樣,但是實際上是相同的
//每隊聲明的是同一個函數
record lookup(const Account &acct);
record lookup(const Account &); //省略了形參的名字
typedef Phone Telno;
record lookup(const Phone&);
record lookup(const Telno&); //Phone Telno類型一樣
重載和const形參
- 頂層const不影響傳入函數的對象。一個擁有頂層const的形參無法和一個沒有頂層const的形參區分開來。
record lookup(Phone);
record lookup(const Phone);//重復聲明
record lookup(Phone*);
record lookup(Phone* const);//重復聲明
- 如果形參是某種類型的指針或者引用,則通過區分其指向的是常量對象還是非常量對象實現函數的重載,這個時候const是底層
//對于接受引用或者指針的函數而言,對象是常量還是非常量對應的形參不同
//定義了四個獨立的重載函數
record lookup(Account&);//函數作用于Account的引用
record lookup(const Account&);//新函數,作用于常量的引用
record lookup(Account*);//新函數,函數作用于Account的指針
record lookup(const Account*);//新函數,作用于指向常量的指針
- 編譯器可以通過實參是否是常量來推斷調用哪個函數。因為const不可以轉化成其他的類型,所以只可以將const對象(指向const的指針)傳遞給const的形參。
- 非常量可以轉化成const,所以上面的四個函數都能作用于非常量對象或者指向非常量對象的指針。當傳遞一個非常量對象或者非常量對象的指針的時候,編譯器會優先選用非常量版本的函數。
建議:何時不應該使用重載函數
- 最好用于指向那些確實非常相似的操作
const_cast和重載
- 先前
//跳出兩個string對象中較短的那個,并且返回其引用
const string &shorterString(const string &s1,const string &s2){return s1.size() < s2.size() ? s1 : s2;
}
- 引入一個新的函數,相較于先前的函數,當他的實參不是常量的時候,得到的結果是一個普通的引用,使用const_cast就可以實現這一點。
- 改進
string &shorterString(string &s1,string &s2){auto &r = shorterString(const_cast<const string&>(s1),const_cast<string&>(s2));return const_cast<string&>(r);
}int main(){string s1 = "Hello";string s2 = "Hello World";cout << shorterString(s1,s2) << endl;}
- 改進是在先前的基礎上進行封裝了一層,里面還是調用了先前的版本。
- 改進的版本,首先是將他的實參強制類型轉化成對于const的引用,然后調用了先前的版本,由先前的版本返回對于const string的引用,這個引用事實上綁定在某個函數初始的非常量的實參上。因此,可以再次將其轉化成一個普通的string&,這個過程顯然是安全的。
調用重載的函數
- 在對于重載的函數進行使用的過程需要一個函數匹配的的過程,這個過程是把函數的調用和一組重載函數中的某一個關聯起來,函數的重載也叫重載確定。
- 編譯器會根據調用的實參與重載集合中的每一個函數的形參進行匹配,然后根據比較的結果決定到底使用哪個函數
- 但是有些情形,對于重載函數的比較會很難,比如,當兩個重載函數參數的數量相同和參數的類型可以相互轉化的時候。
匹配的結果
- 1,編譯器找到一個與實參最佳匹配的函數,并生成調用這個函數的代碼。
- 2,找不到一個函數和調用的實參相互匹配,此時編譯器會提示無匹配的錯誤
- 3,對于一個函數可以匹配,但是每一個都不是最佳的選擇,這個時候也是錯誤,稱為二義性調用。
6.4.1 重載和作用域
- 重載對于函數的作用域并沒有什么特別的改變,如果在內層作用域中聲明名字,它將隱藏外層作用域中聲明的同名的實體。在不同的作用域中無法重載函數的名字。
- 原則:將函數的聲明置于局部的作用域之內不是一個明智的決定,這里僅僅為了舉例子
void print(const string &);
void print(double );//重載print函數
void fooBar(int ival){
// bool read = false; //新的作用域:隱藏外層的read,如果把這一行注釋掉,那么string s = read();就不會出錯string s = read(); //如果上面一行存在,會出錯,因為read是一個布爾值,而不是函數cout << s << endl;
}
int main(){fooBar(1);
}
?