1. 程式人生 > >《隨筆十三》—— C++中的 “ 名字查詢與類的作用域 ”

《隨筆十三》—— 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 類的成員
}