每當一個變量或者一個對象出現,編譯器都會進行名字查找(name lookup),以確認這個變量或對象的具體屬性。通常狀況下,程序會從變量出現的地方開始向上查找,由內向外查找各級做用域直到全局做用域,找到同名的變量聲明即中止,若最終沒找到則報錯。ios
#include <iostream> using namespace std; int a = 0; int main() { string a = "1"; cout << a << endl; // 在main函數體內找到了a的聲明,中止名字查找,輸出爲string的內容「1」 return 0; }
名字查找還受到做用域限定符::
的影響,若變量以前出現了做用域限定,那麼則會從限定的做用域內進行全局搜索,而且不會查找其餘位置。若是::
以前沒有出現做用域名,那麼就會在全局做用域查找。函數
#include <iostream> using namespace std; int a = 0; int main() { string a = "1"; cout << ::a << endl; // 限定a的查找從全局做用域開始,那麼首先被找到的是a的int類型定義,程序輸出0 return 0; }
此外,因爲在找到同名聲明以後便會中止查找,函數匹配出如今名字查找以後。spa
#include <iostream> using namespace std; void print() { cout << "1" << endl; } int main() { int print; print(); // 試圖調用print(),但程序沒法經過編譯,這是由於在找到int型變量print以後, // 編譯器就中止了查找,把一個變量看成函數調用顯然是錯誤的 return 0; }
從上面的例子能夠看出,在不使用訪問限定符的狀況下,內層做用域的變量會隱藏外層做用域的同名變量,即發生了名字隱藏。名字隱藏還會發生在類的繼承過程,在子類定義和父類同名的成員時,會隱藏從父類繼承來的成員,名字隱藏並非什麼特性,相反,名字隱藏會增長程序編寫的難度。咱們應該養成良好的命名習慣,避免出現名字隱藏。code
在函數調用時,首先對函數進行名字查找,在找到一個同名函數(假設不會出現命名衝突)時,編譯器並不會中止查找,而是繼續找出當前做用域的全部重載函數,而後根據最佳匹配規則,肯定實際調用的函數。不一樣做用域的同名函數並不會被視爲重載。最直觀的例子是,類內的函數與類外的同名函數並不會造成重載。對象
在名字查找完成且不存在編譯錯誤的狀況下,編譯器便開始肯定與調用匹配得最好的函數。最佳匹配原則也很簡單,實參類型與形參類型越接近,匹配得越好。繼承
void print(int a) { cout << "int" << endl; } void print(double a) { cout << "double" << endl; } print(1); // 1是int類型,與print的int形參重載構成最佳匹配,故程序輸出「int」
但若是同時有多個函數與調用造成匹配,那麼編譯器將報錯。編譯器不容許有二義性的調用存在。作用域
void print(int a, int b, int c) { cout << "int" << endl; } void print(double a, double b, double c) { cout << "double" << endl; } print(1, 1.0, 1); // 編譯器指出有多個函數重載匹配