c++ 類作用域中的名字查詢

PengPengBlog發表於2017-04-05

一.類成員宣告的名字查詢 

按以下方式確定在類成員的宣告中用到的名字  

什麼叫做類成員宣告的名字呢? 就是宣告一個函式用到的型別。 比如  doube max(x, y) 其實double 就是類成員宣告的名字。

* 先檢查出現在名字使用之前的類成員宣告,而這個之前是作用域內之前(1.全域性作用域的之前;2.區域性作用域的之前)

* 如果第一查詢不成功,則檢查包含類定義的作用域中出現的宣告以及出現在類定義之前的宣告.

例如:

typedef double Money;  
class Account  
{  
   public:  
      Money balance() { return bal;}  
    private:  
        Money bal;  
 };  

在處理balance函式的宣告時,編譯器首先在類Account中查詢Money的宣告,編譯器只考慮出現在Moeny使用之前的宣告,因為找不到任何成員宣告,編譯器隨後在全域性作用域中查詢Money的宣告.找到了Money的宣告,並將它用作balance()函式的返回型別 和 資料成員bal的型別.

通常,名字必須在使用之前進行定義. 而且,如果一個名字被用作型別名, 該名字就不能重複定義:

typedef double Money;  
class Account  
{  
   public:  
      Money balance() { return bal;
}  
typedef long double Money;
Money bal;
上面的程式碼回報錯,因為重複定義了Money.

二.在類成員定義中的名字查詢

類定義的名字查詢: 比如int pg; pg 就是類成員定義中的名字。
按以下方式確定在成員函式的函式體中用到的名字

* 首先檢測成員函式區域性作用域的宣告

*如果在成員函式中找不到該名字的宣告, 則檢查對所有類成員的宣告

*如果在類中找不到該名字的宣告,則檢查在此成員函式定義之前的作用域中出現的宣告

例如:

const int pg=10;  
class A  
{  
   public:  
      int  fun(int  pg)  
      {  
          return pg;  //哪個pg?  
      }  
   private:  
      int  pg;  
};  

上面的程式碼中fun()函式內return 的pg是哪個pg呢?  可以測試一下:

int main()  
{  
  int x;  
  A test;  
  x=test.fun(3);  
    
  cout<<x<<endl;  
  return 0;  
}   

執行結果是三,說明函式fun()返回的是形參宣告的那個pg.

查詢fun的定義中使用的名字pg時, 編譯器首先在該函式的區域性作用域中查詢. 函式區域性作用域中宣告瞭一個函式形參.查詢成功, 形參pg會遮蔽名為pg的成員.

儘管類的成員被遮蔽了,仍然可以通過類名來限定成員名或顯示使用this指標來使用它.

int fun(double pg)  
{  
   return this->pg;  
   //return  A::pg;  
}  

函式作用域之後,在類作用域中查詢

如果想要使用pg成員, 更好的方式是為形參取一個不同的名字:

int fun ( int b)
{
   return pg;
}
現在編譯器會在A類中查詢該名字,儘管pg是現在fun中使用,然後在宣告, 編譯器還是確定這裡用的是名為pg的資料成員. 所以用this指標和改變形參的名字都可以做到在類中查詢。

類作用域之後,在外圍作用域中查詢:

如果編譯器不能在函式或作用域中找到,就在外圍作用域中查詢. 上面的例子中, 在類A定義之前的全域性作用域中宣告瞭一個名為pg的全域性物件, 然後, 它被遮蔽了..

儘管全域性物件被遮蔽了,但通過全域性作用域確定操作符來限定名字,仍然可以使用它.

#include <iostream>  
using namespace std;  
const int   pg=10;  
class A  
{  
   public:  
      int  fun(double pg)  
      {  
          return  ::pg;  //這裡使用全域性作用域  
            
      }  
       
      A() : pg(0) { };  
   private:  
      double pg;  
};  
  
int main()  
{  
  int x;  
  A test;  
  x=test.fun(3);  
    
  cout<<x<<endl;  
  return 0;  
}   


執行結果為10.

最後用課後習題總結一下:

解釋下述程式碼.指出每次使用Type或initVal時用到的是哪個名字定義.如果存在錯誤,說明如何改正.

typedef string Type;
Type initVal();

class Exercise
{
   public: 
      typedef double Type;  
      Type setVal( Type);//類宣告的查詢,type 指的是 double; 類定義的查詢,這裡type 指的是還是double
      Type initVal(); //這裡面類宣告的查詢type 也指的是double 
    private:
       int val;
};

Type Exercise::setVal( Type parm) //;類宣告的查詢這裡type指的是string, 類定義的查詢 type指的是string, 
{//為什麼是string呢? 因為它僅僅查詢函式只考慮它使用之前的宣告,而這個之前是不僅僅是位置上的之前,也是作用
域的之前,這個type屬於全域性作用域的之前。
   val = parm + initVal(); //這裡initVal() 沒有函式體
}

 在Exercise類的定義體內, 成員函式setVal和initVal的宣告中作為形參型別和返回型別的Type所使用的都是Exercise內部定義的型別別名Type.
  在Exercise類的定義體之外成員函式setVal的定義中, 作為形參型別的Type,使用的是Exercise內部定義的型別別名Type,作為函式返回型別的Type,使用的是全域性型別的Type.
在Exercise類的成員函式setVal的定義中使用的initVal,用到的是Exercise類的成員函式.

成員函式setVal的定義有錯, 編譯器會認為Exercise類中存在兩個相互過載的成員函式setVal,但這兩個函式的區別僅在於返回型別不同,不能構成合法的函式過載,因此有錯,而且該漢化素應返回一個值, 可更正為


Exercise::Type Exercise::setVal( Type parm)
{
  val=parm  + initVal();
  retrun val;
}

注:形參表和函式體處於類作用域中,而函式返回型別不一定在類作用域中, 所以要在返回型別面前加上作用域運算子.





相關文章