<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在C++類別的成員函數中,名字查詢是按照由內到外進行的。首先查詢成員函數中的名字,之後再查詢類中定義的名字,最後查詢類外定義的名字。
自定義類MyClass程式碼如下
int i = 1; class MyClass { public: int i = 2; void myFunc() { int i = 3; int j = i; } };
其中,在MyClass類的成員函數myFunc()中使用了變數i,而在myFunc()函數、MyClass類以及類外都定義了變數i,此時myFunc()成員函數會根據由內向外的順序對i進行查詢,即此時j的值應該是3。
使用類的變數
在myFunc()中如果需要使用類myFunc()函數的變數i,可以進行如下定義
int j = MyClass::i;
或者
int j = this->i;
此時,表示使用MyClass中定義的變數i,j的值是2。
使用類外變數
在myFunc()中如果需要使用類外定義的變數i,可以進行如下定義
int j = ::i;
此時,j的值是1。
每個類都會定義它自己的作用域。在類的作用域之外,普通的資料和函數成員只能由物件、參照或者指標使用成員存取運運算元來存取。對於類型別成員則使用作用域運運算元存取。不論哪種情況,跟在運運算元之後的名字都必須是對應類的成員。
Screen::pos ht = 24, wd = 80; // 使用 Screen 定義的 pos 型別 Screen scr(ht, wd, ' '); Screen *p = &scr; char c = scr.get(); // 存取 scr 物件的 get 成員 c = p->get(); // 存取 p 所指物件的 get 成員
類的作用域 (class scope) 每個類定義一個作用域。類作用域比其他作用域更加複雜,類中定義的成員函數甚至有可能使用定義語句之後的名字。
一個類就是一個作用域,我們在類的外部定義成員函數時必須同時提供類名和函數名。在類的外部,成員的名字被隱藏起來了。一旦遇到了類名,定義的剩餘部分就在類的作用域之內了,這裡的剩餘部分包括參數列和函數體。結果就是,我們可以直接使用類的其他成員而無須再次授權了。
void Window::clear(ScreenIndex i) { Screen &s = screens[i]; s.contents = string(s.height * s.width, ' '); }
編譯器在處理參數列之前已經明確了我們當前正位於 Window 類的作用域中,所以不必再專門說明 ScreenIndex 是 Window 類定義的。出於同樣的原因,編譯器也能知道函數體中用到的 screens 也是在 Window 類中定義的。
函數的返回型別通常出現在函數名之前。因此當成員函數定義在類的外部時,返回型別中使用的名字都位於類的作用域之外。這時,返回型別必須指明它是哪個類的成員。我們可能向 Window 類新增一個新的名為 add_screen 的函數,它負責向顯示器新增一個新的螢幕。這個成員的返回型別將是 ScreenIndex,使用者可以通過它定位到指定的 Screen。
class Window { public: // 向視窗新增一個 Screen,返回它的編號 ScreenIndex add_screen(const Screen &); ...... }; // 首先處理返回型別,之後我們才進入 Window 的作用域 Window::ScreenIndex Window::add_screen(const Screen &s) { screens.push_back(s); return screens.size() - 1; }
因為返回型別出現在類名之前,所以事實上它是位於 Window 類的作用域之外的。在這種情況下,要想使用 Screenlndex 作為返回型別,我們必須明確指定哪個類定義了它。
名字查詢 (name lookup) (尋找與所用名字最匹配的宣告的過程) 的過程比較直截了當。
對於定義在類內部的成員函數來說,解析其中名字的方式與上述的查詢規則有所區別,不過在當前的這個例子中體現得不太明顯。
類的定義分兩步處理:
編譯器處理完類中的全部宣告後才會處理成員函數的定義。按照這種兩階段的方式處理類可以簡化類程式碼的組織方式。因為成員函數體直到整個類可見後才會被處理,所以它能使用類中定義的任何名字。
名字查詢 (name lookup) 是根據名字的使用尋找匹配的宣告的過程。
用於類成員宣告的名字查詢
這種兩階段的處理方式只適用於成員函數中使用的名字。宣告中使用的名字,包括返回型別或者參數列中使用的名字,都必須在使用前確保可見。如果某個成員的宣告使用了類中尚未出現的名字,則編譯器將會在定義該類的作用域中繼續査找。
typedef double Money; std::string bal; class Account { public: Money balance() { return bal; } private: Money bal; };
當編譯器看到 balance 函數的宣告語句時,它將在 Account 類的範圍內尋找對 Money 的宣告。編譯器只考慮 Account 中在使用 Money 前出現的宣告,因為沒找到匹配的成員,所以編譯器會接著到 Account 的外層作用域中查詢。在這個例子中,編譯器會找到 Money 的 typedef 語句,該型別被用作 balance 函數的返回型別以及資料成員 bal 的型別。balance 函數體在整個類可見後才被處理,該函數的 return 語句返回名為 bal 的成員,而非外層作用域的 std::string 物件。
型別名要特殊處理
內層作用域可以重新定義外層作用域中的名字,即使該名字已經在內層作用域中使用過。然而在類中,如果成員使用了外層作用域中的某個名字,而該名字代表一種型別,則類不能在之後重新定義該名字。
typedef double Money; class Account { public: Money balance() { return bal; } // 使用外層作用域的 Money private: typedef double Money; // 錯誤:不能重新定義 Money Money bal; };
需要特別注意的是,即使 Account 中定義的 Money 型別與外層作用域一致,上述程式碼仍然是錯誤的。儘管重新定義型別名字是一種錯誤的行為,但是編譯器並不為此負責。一些編譯器仍將順利通過這樣的程式碼,而忽略程式碼有錯的事實。型別名的定義通常出現在類的開始處,這樣就能確保所有使用該型別的成員都出現在類名的定義之後。
成員定義中的普通塊作用域的名字查詢
成員函數中使用的名字按照如下方式解析:
一般來說,不建議使用其他成員的名字作為某個成員函數的引數。為了更好地解釋名字的解析過程,我們不妨在 dummy_fcn 函數中暫時違反一下這個約定。
// 這段程式碼僅為了說明而用,不是一段很好的程式碼 // 通常情況下不建議為引數和成員使用同樣的名字 int height; // 定義了一個名字,稍後將在 Screen 中使用 class Screen { public: typedef std::string::size_type pos; void dummy_fcn(pos height) { cursor = width * height; // height 是那個引數 } private: pos cursor = 0; pos height = 0, width = 0; };
當編譯器處理 dummy_fcn 中的乘法表示式時,它首先在函數作用域內查詢表示式中用到的名字。函數的引數位於函數作用域內,因此 dummy_fcn 函數體內用到的名字 height 指的是引數宣告。在此例中,height 引數隱藏了同名的成員。如果想繞開上面的查詢規則,應該將程式碼變為:
// 不建議的寫法:成員函數中的名字不應該隱藏同名的成員 void Screen::dummy_fcn(pos height) { cursor = width * this->height; // 成員 height // 另外一種表示該成員的方式 cursor = width * Screen::height; // 成員 height }
儘管類的成員被隱藏了,但我們仍然可以通過加上類的名字或顯式地使用 this 指標來強制存取成員,其實最好的確保我們使用 height 成員的方法是給引數起個其他名字。
// 建議的寫法:不要把成員名字作為引數或其他區域性變數使用 void Screen::dummy_fcn(pos ht) { cursor = width * height; // 成員 height }
在此例中,當編譯器查詢名字 height 時,顯然在 dummy_fcn 函數內部是找不到的。編譯器接著會在 Screen 內查詢匹配的宣告,即使 height 的宣告出現在 dummy_fcn 使用它之後,編譯器也能正確地解析函數使用的是名為 height 的成員。
類作用域之後,在外圍的作用域中查詢
如果編譯器在函數和類的作用域中都沒有找到名字,它將接著在外圍的作用域中查詢。在我們的例子中,名字 height 定義在外層作用域中,且位於 Screen 的定義之前。 然而,外層作用域中的物件被名為 height 的成員隱藏掉了。如果我們需要的是外層作用域中的名字,可以顯式地通過作用域運運算元來進行請求。
// 不建議的寫法:不要隱藏外層作用域中可能被用到的名字 void Screen::dummy_fcn(pos height) { cursor = width * ::height; // height 是哪個全域性的 }
儘管外層的物件被隱藏掉了,但我們仍然可以用作用域運運算元存取它。
在檔案中名字的出現處對其進行解析
當成員定義在類的外部時,名字査找的第三步不僅要考慮類定義之前的全域性作用域中的宣告,還需要考慮在成員函數定義之前的全域性作用域中的宣告。
int height; // 定義了一個名字,稍後將在 Screen 中使用 class Screen { public: typedef std::string::size_type pos; void setHeight(pos); pos height = 0; // 隱藏了外層作用域中的 height }; Screen::pos verify(Screen::pos); void Screen::setHeight(pos var) { // var: 引數 // height: 類的成員 // verify: 全域性函數 height = verify(var); }
全域性函數 verify 的宣告在 Screen 類的定義之前是不可見的。名字査找的第三步包括了成員函數出現之前的全域性作用域。在此例中,verify 的宣告位於 setHeight 的定義之前,因此可以被正常使用。
以上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45