《隨筆十三》—— C++中的 “ 名字查詢與類的作用域 ”
名字查詢與類的作用域 (Primer 254)
● 名字查詢:尋找與所用名字最匹配的宣告的過程:
首先,在名字所在的塊中尋找其宣告語句, 只考慮在名字的使用之前出現的宣告。
如果沒有找到, 繼續查詢外層作用域。
如果最終沒有找到匹配的宣告,則程式報錯。
定義在類內部的成員函式,解析其中的名字方式與上述查詢規則有所區別,類的定義分兩步:
首先,編譯成員的宣告
直到類全部可見後才編譯函式體
意思就是說,編譯器處理完類中的全部聲明後才會處理成員函式函式定義
通常非類作用域的符號查詢是按順序的,其後面的被涉及的符號必須在前面出現過.如: void foo() { }; void main() { int a= 1, b = 2; int c = a + b; //涉及的a和b在前面出現過了. int d = foo(); //涉及的foo在前面出現過了. c = d + goo(); //編譯報錯,因為涉及的goo沒有在前面出現過(其在本條語句的後面第一次出現) } void goo() { } ////////////////////////////////////////////////////////////////////////////////////////////// 對於類作用域(即類成員)的符號,我們不必要符號順序出現,如: class CFoo { public: void hoo() { int c = m_aData + m_bData; //可以編譯通過,雖然m_aData, m_bData宣告在本條語句的後面,但是因為是類作用域,在類中都可見,所以這裡是可以的. } private: int m_aData,m_bData; }; 那麼編譯器是怎麼實現類作用域的符號查詢的呢? 分為兩步編譯: 第一步: .首先值編譯類的所有成員的宣告(而不是定義),如void CFoo::hoo()就是一個成員函式的宣告,而其的函式體就是起定義. 這樣的話,在此步遍編譯結束後,編譯器會把所有的符號宣告儲存起來,供第二步使用. 第二步: 在編譯成員的定義(如函式體),此時如果遇到一個變數如m_aData,就會首先從第一步中的儲存查詢。
● 按照這種兩階段的方式處理類可以簡化類程式碼的組織方式。 因為成員函式體直到整個類可見後才會被處理, 所以它能使用類中定義的任何名字。 相反,如果函式的定義和成員的宣告被同時處理,那麼我們不得不在成員函式中使用那些早已出現的名字
● 注意: 這種兩階段的處理方式只適用於成員函式中的名字。 宣告中使用的名字, 包括返回型別或者引數列表中使用的名字, 都必須在使用前確保可見。 如果某個成員的宣告使用了類中尚未出現的名字, 則編譯器將會在定義該類的作用域中繼續查詢。
型別名要特殊處理
●一般來說,內層作用域可以定義外層作用域的名字(隱藏了外層的名字) ,即使改名字已經在內層作用域使用過。在類中,如果成員使用了外層作用域中的某個名字, 而且該名字代表一種型別, 則類不能在之後重新定義該名字:
typedef double Money;
class Account
{
public:
Money balance () //使用外層作用域的Money
{
return bal;
}
private:
typedef double Money; // 錯誤:不能重新定義Money,但是在VS上編譯是正確的,但行為確實錯誤的。
Money bal;
};
注意: 型別名的定義通常出現在類的開始處,這樣就能確保所有使用該型別的成員都出現在類名的定義之後。
成員定義中的普通塊作用域的名字查詢
● 成員函式中使用的名字按照如下方式解析:
首先, 在成員函式內查詢該名字的宣告。 和前面一樣, 只有在函式使用之前出現的宣告才被考慮。
如果在成員函式內沒有找到,則在類內繼續查詢,這時類的所有成員都可以被考慮。
如果類內也沒找到該名字的宣告,在成員函式定義之前的作用域內繼續查詢。
● 注意: 通常情況下不建議為引數(或者說區域性變數)和成員使用同樣的名字
在檔案中名字的出現處對其進行解析
● 當成員定義在類的外部時,不僅要考慮類定義之前的全域性作用域中的宣告,還需要考慮在成員函式定義之前的全域性作用域中的宣告。
int height;
class Screen
{
public:
typedef string::size_type pos;
void setHeight(pos);
pos height = 0; //隱藏了外層作用域中的height
};
Screen::pos verify(Screen::pos);
void Screen::setHeight(pos var)
{
height = verify(var); //height 類的成員
}