第七章_类_7.4 类的作用域(加了自己的总结部分)
每個類都會定義自己的作用域。
在類的作用域之外,普通的數據和函數成員只能由對象、引用或者指針使用成員訪問運算符來訪問。對于類類型成員則使用作用域運算符訪問。不論哪種情況,跟在運算符之后的名字都必須是對應類的成員:
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 成員
作用域和定義在類外部的成員
一個類就是一個作用域。在類的外部,成員的名字被隱藏起來了。
一旦遇到了類名,定義的剩余部分就在類的作用域之內了。這里的剩余部分包括參數列表和函數體。
函數的返回類型通常出現在函數名之前。因此當成員函數定義在類的外部時,返回類型中使用的名字都位于類的作用域之外。這時,返回類型必須指明它是哪個類的成員。
例如,在 Window_mgr 類添加一個 addScreen 的函數,負責向顯示器添加一個新的屏幕。
class Window_mgr { public:// 向窗口添加一個 Screen,返回它的編號ScreenIndex addScreen(const Screen&); }; // 首先處理返回類型,之后我們才進入 Window_mgr 的作用域 Window_mgr::ScreenIndex? Window_mgr::addScreen(const Screen &s){screens.push_back(s);return screens.size() - 1; }
7.4.1 名字查找與類的作用域
名字查找(name lookup): 尋找與所用名字最匹配的聲明的過程。
一般是以下步驟:
首先,在名字所在的塊中尋找其他聲明語句,只考慮在名字使用之前出現的聲明
如果沒有找到,繼續查找外層作用域
如果最終沒有找到匹配的聲明,則程序報錯。
類成員函數的定義分以下兩步處理名字查找
首先,編譯成員的聲明
直到類全部可見后才編譯函數體
編譯器處理完類中全部聲明后才會處理成員函數的定義。
用于類成員聲明的名字查找
聲明中使用的名字,包括返回類型或者參數列表中使用的名字,都必須在使用前確保可見。
如果某個成員的聲明使用了類中尚未出現的名字,則編譯器將會在定義該類的作用域中繼續查找。
typedef doubel Money; string bal; class Account{ public:Money balance() { return bal; } // 這里函數的返回類型使用的是外層作用域的Money// return返回的是類內定義的成員bal,而非外層作用域的的string對象 private:Money bal; }
類型名要特殊處理
在類中,如果成員使用了外層作用域中的某個名字,而該名字代表一種類型,則類不能在之后重新定義該名字
typedef doubel Money; string bal; class Account{ public:Money balance() { return bal; }? private:Money bal;typedef double Money; // 錯誤,不能重新定義Money }
類型名的定義通常出現在類的開始處,這樣就能確保所有使用該類型的成員都出現在類名之后
成員定義中的普通塊作用域的名字查找
成員函數中使用的名字按照如下方式解析:
首先,在成員函數內查找該名字的聲明。只有在函數使用之前出現的聲明才被考慮
如果在成員函數內沒有找到,則在類內繼續查找,這是類的所有成員都可以被考慮
如果類內也沒有找到該名字的聲明,在成員函數定義之前的作用域內繼續查找
// 注意:這段代碼僅為了說明而用,不是一段很好的代碼
// 一般不建議使用其他成員的名字作為某個成員函數的參數
int 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 的定義之前,因此可以正常使用。
自己的總結的幾點:
1.如果返回類型使用由類定義的類型,則必須使用完全限定名。 例如
class Screen { public: typedef std::string::size_type index; index get_cursor() const; }; inline Screen::index Screen::get_cursor() const { return cursor; }這種用法也可以適用于類的公有數據成員,但是我自己驗證只能在成員函數的定義中(類作用域)使用,別的作用域不起作用。所以類的成員運算符可以在任何地方訪問(當然是公有的),但是必須加類名去限定。類的共有數據成員在任何地方可以使用成員運算符.使用,在如果是在類作用域,也可以使用::訪問類共有成員。
2.名字查找
(1)沒有類的時候,在我們所編寫的程序中, 名字查找(尋找與給定的名字使用相匹配的聲明的過程)是相對直接的。
????????1. 首先,在使用該名字的塊中查找名字的聲明。只考慮在該項使用之前聲明的名字。
????????2. 如果找不到該名字,則在包圍的作用域中查找。
(2)類成員聲明的名字查找
????????1. 檢查出現在名字使用之前的類成員的聲明。
????????2. 如果第 1 步查找不成功,則檢查包含類定義的作用域中出現的聲明以及出現在類定義之前的聲明。
(3)類成員定義中的名字查找
????????1. 首先檢查成員函數局部作用域中的聲明。(包括函數形參列表)
????????2. 如果在成員函數中找不到該名字的聲明,則檢查對所有類成員的聲明。(對于類內部數據成員,即使先使用,然后才有聲明,也是正確的)
????????3. 如果在類中找不到該名字的聲明, 則檢查在此成員函數定義之前的作用域中出現的聲明。
????????如果想使用全局作用域中被遮掩的變量,使用作用域名::,::height(使用全局height)
????????當成員定義在類定義的外部時,名字查找的第 3 步不僅要考慮在 Screen類定義之前的全局作用域中的聲明, 而且要考慮在成員函數定義之前出現的全局作用域聲明:
class Screen { public: // ... void setHeight(index); private: index height; }; Screen::index verify(Screen::index); void Screen::setHeight(index var) { // var: refers to the parameter // height: refers to the class member // verify: refers to the global function height = verify(var); }
????????全局函數 verify 的聲明在 Screen 類定義之前是不可見的。然而,名字查找的第 3 步要考慮那些出現在成員定義之前的外圍作用域聲明,并找到全局函數 verify 的聲明。
????????typedef也有類似于變量定義覆蓋的情況
(4)另外,對于一個函數,只要聲明之后且有定義就可以使用;要定義一個類類型,只要在類全部定義后(類的定義大括號之后就是定義完成)就可以。注意.h中不要有定義,.h通常得編譯很多次,包括變量的定義都不要有,實在需要可以在.cc文件中定義。不然在.h中文件中定義就會出現多次定義的錯誤,即時你只定義了一次,可能編譯器多次編譯.h文件,所以你仍然會有錯誤。
(5)
類作用域中的名字查找(尋找與給定的名字使用相匹配的聲明的過程)
C++中,所有名字使用之前必須聲明。 編譯器實際上分兩個階段處理類定義: 1.首先編譯類成員聲明 2.只有在所有的成員出現之后,才編譯它們的定義 一. 類成員聲明的名字查找 ????typedef double money; ????class account { ??????????public: ?????????????????money?balance(); ??????????private: ?????????????????money bal; ????}; a.遇到money,且money在類成員聲明中 b.查找類中money出現之前的聲明,找出是否有money聲明 c.未找到,跳出類,在account類之前的全局作用域找money聲明 二.類成員定義中(函數體)的名字查找 ?1.此定義在類體中 ???a.在函數體中,名字出現之前的位置找出聲明。 ???b.沒找到,跳出函數體,查找類中的所有聲明。 ???c.沒找到,在類之前的全局作用域找聲明。 ? ?2.此定義在類體外 ???a.在函數體中,名字出現之前的位置找出聲明。 ???b.沒找到,跳出函數體,查找類中的所有聲明。 ???c.在函數定義之前的全局作用域中查找聲明。 class Screen { ??????public: ?????????????typedef double index; ?????????????void setHeight(index); ??????private: ?????????????index height; }; Screen::index verify(Screen::index); void Screen::setHeight(index var) { ?????height = verify(var); } 全局函數verify的聲明在screen類定義之前是不可見的,然而根據第三步考慮成員函數出現之前的所有全局作用域,就會找到全局函數verify的聲明。另一個例子在這個頁面,自己隨意寫的,放在這里可以參考下:
https://blog.csdn.net/digitalkee/article/details/116540193
總結
以上是生活随笔為你收集整理的第七章_类_7.4 类的作用域(加了自己的总结部分)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: pygame.rect中 Rect类 属
- 下一篇: 动态分配的const对象