可以在另一個類內部(與後面所講述的局部類不同,嵌套類是在類內部)定義一個類,這樣的類是嵌套類,也稱為嵌套類型。嵌套類最常用於定義執行類.
嵌套類是獨立的類,基本上與它們的外圍類不相關,因此,外圍類和嵌套類的對象是互相獨立的。嵌套類型的對象不具備外圍類所定義的成員,同樣,外圍類的成員也不具備嵌套類所定義的成員。
嵌套類的名字在其外圍類的作用域中可見,但在其他類作用域或定義外圍類的作用域中不可見。嵌套類的名字將不會與另一作用域中聲明的名字沖突
嵌套類可以具有與非嵌套類相同種類的成員。像任何其他類一樣,嵌套類使用訪問標號控制對自己成員的訪問。成員可以聲明為 public、private 或 protected。外圍類對嵌套類的成員沒有特殊訪問權,並且嵌套類對其外圍類的成員也沒有特殊訪問權。
嵌套類定義了其外圍類中的一個類型成員。像任何其他成員一樣,外圍類決定對這個類型的訪問。
[實例]
將 QueueItem 類設為 Queue 類的 private 成員,那樣,Queue 類(及其友元)可以使用 QueueItem,但 QueueItem 類類型對普通用戶代碼不可見。一旦 QueueItem 類本身為 private,我們就可以使其成員為 public 成員--只有 Queue 或 Queue 的友元可以訪問 QueueItem 類型,所以不必防止一般程序訪問 QueueItem 成員。通過用保留字 struct 定義 QueueItem 使成員為 public 成員。
新的設計如下:
templateclass Queue { public: //... private: struct QueueItem { QueueItem(const Type &); Type item; QueueItem *next; }; QueueItem *head; QueueItem *tail; };
1.嵌套在類模板內部的類是模板
因為Queue是模板,因此它的成員也是模板,而且QueueItem的模板形參與其外圍類Queue的模板形參相同.
Queue 類的每次實例化用對應於 Type 的適當模板實參產生自己的QueueItem 類。QueueItem 類模板的實例化與外圍 Queue 類模板的實例化之間的映射是一對一的。
2.定義嵌套類的成員
在其類外部定義的嵌套類成員,必須定義在定義外圍類的同一作用域中。在其類外部定義的嵌套類的成員,不能定義在外圍類內部,嵌套類的成員不是外圍類的成員。
QueueItem 類的構造函數不是 Queue 類的成員,因此,不能將它定義在Queue 類定義體中的任何地方.
templateQueue ::QueueItem::QueueItem(const Type &t): item(t),next(0) {}
因為Queue和QueueItem是模板,因此該構造函數也為模板.
3.在外圍類外部定義嵌套類
嵌套類通常支持外圍類的實現細節。我們可能希望防止外圍類的用戶看見嵌套類的實現代碼。
例如,我們可能希望將 QueueItem 類的定義放在它自己的文件中,我們可以在 Queue 類及其成員的實現文件中包含這個文件。正如可以在類定義體外部定義嵌套類的成員一樣,我們也可以在外圍類定義體的外部定義整個嵌套類:
templateclass Queue { public: //... private: struct QueueItem; QueueItem *head; QueueItem *tail; }; template struct Queue ::QueueItem { QueueItem(const Type &t):item(t),next(0) {} Type item; QueueItem *next; };
注意,我們必須在Queue類體內部聲明QueueItem類.
[小心]
在看到在類定義體外部定義的嵌套類的實際定義之前,該類是不完全類型,應用所有使用不完全類型的常規限制。
4.嵌套類靜態成員定義
如果QueueItem類聲明了一個靜態成員,它的定義也需要放在外層作用域中,假定QueueItem有一個靜態成員:
templateint Queue ::QueueItem::static_mem = 1024;
5.使用外圍類的成員
外圍作用域的對象與其嵌套類型的對象之間沒有聯系
templatevoid Queue ::pop() { QueueItem *q = head; head = head -> next; delete q; }
Queue 類型的對象沒有名為 item 或 next 成員。Queue 類的函數成員可以使用 head 和 tail 成員(它們是指向 QueueItem 對象的指針)來獲取那些QueueItem 成員。
6.使用靜態成員或其他類型的成員
嵌套類可以直接引用外圍類的靜態成員,類型名和枚舉成員[同後面所講的局部類].然而,引用外圍類作用域之外的類型名或靜態成員,需要作用域確定操作符.
7.嵌套模板的實例化
實例化外圍類模板的時候,不會自動實例化類模板的嵌套類。像任何成員函數一樣,只有當在需要完整類類型的情況下使用嵌套類本身的時候,才會實例化嵌套類。例如,像
Queueqi;
這樣的定義,用 int 類型實例化了 Queue 模板,但沒有實例化QueueItem
只有在使用 QueueItem
當處理類成員聲明的時候,所用的名字必須在使用之前出現;當處理定義的時候,整個嵌套類和外圍類均在作用域中.
class Outer { public: struct Inner { void process(const Outer &); //OK Inner2 val; //Error }; struct Inner2 { public: Inner2(int i = 0):val(i) {} void process(const Outer &out) //OK { out.handle(); } private: int val; }; void handle() const; };
[說明]
編譯器首先處理 Outer 類成員的聲明 Outer::Inner 和 Outer::Inner2。將名字 Outer 作為 Inner::process 形參的使用被綁定到外圍類,在看到process 的聲明時,那個類仍是不完整的,但形參是一個引用,所以這個使用是正確的。
數據成員 Inner::val 的聲明是錯誤的,還沒有看到 Inner2 類型。
Inner2 中的聲明看來沒有問題--它們大多只使用內置類型 int。唯一的例外是成員函數 process,它的形參確定為不完全類型 Outer。因為其形參是一個引用,所以 Outer 為不完全類型是無關緊要的。
直到看到了外圍類中的其余聲明之後,編譯器才處理構造函數和 process 成員的定義。對 Outer 類聲明的完成將函數 handle 的聲明放在作用域中。
當編譯器查找 Inner2 類中的定義所用的名字時,Inner2 類和 Outer 類中的所有名字都在作用域中。val 的使用(出現在 val 的聲明之前)是正確的:將該引用綁定到 Inner2 類中的數據成員[不大理解這一段是什麼意思%>_<%]。同樣,Inner2::process 成員函數體中對 Outer 類的 handle 的使用也正確,當編譯 Inner2 類的成員的時候,整個 Outer 類在作用域中。
使用作用域操作符控制名字查找
可以使用作用域操作符訪問handle的全局版本:
void process(const Outer &out) { ::hadle(out); }