C++嵌套類
可以在另一個類內部定義一個類,這樣的類是嵌套類,也稱為嵌套類型。嵌套類是獨立的類,基本上與它們的外圍類不相關,外圍類對嵌套類的成員沒有特殊訪問權,並且嵌套類對其外圍類的成員也沒有特殊訪問權。
嵌套類的名字在其外圍類的作用域中可見,但在其他類作用域或定義外圍類的作用域中不可見。嵌套類的名字將不會與另一作用域中聲明的名字沖突。
嵌套類定義了其外圍類中的一個類型成員。像任何其他成員一樣,外圍類決定對這個類型的訪問。在外圍類的 public 部分定義的嵌套類定義了可在任何地方使用的類型,在外圍類的 protected 部分定義的嵌套類定義了只能由外圍類、友元或派生類訪問的類型,在外圍類的 private 部分定義的嵌套類定義了只能被外圍類或其友元訪問的類型。
1:例子
template <class Type> class Queue { public: ... private: // public members are ok: QueueItem is a private member of Queue // only Queue and its friends may access the members of QueueItem struct QueueItem { QueueItem(const Type &); Type item; QueueItem *next; }; QueueItem *head; QueueItem *tail; };
因為 QueueItem 類是 private 成員,所以只有 Queue 類的成員和友元可以使用 QueueItem 類型。使 QueueItem 類成為 private 成員之後,就可以使QueueItem 成員 public。
因為 Queue 類是一個模板,它的成員也隱含地是模板。具體而言,嵌套類QueueItem 隱含地是一個類模板。像 Queue 類中任何其他成員一樣,QueueItem的模板形參與其外圍類(Queue 類)的模板形參相同。
在其類外部定義的嵌套類成員,不能定義在外圍類內部,必須定義在定義外圍類的同一作用域中。
成員的名字在類外部是不可見的。要定義QueueItem的構造函數,必須指出,QueueItem 是 Queue 類作用域中的嵌套類:
template <class Type> Queue<Type>::QueueItem::QueueItem(const Type &t): item(t), next(0) { }
這段代碼定義了一個函數模板,以名為 Type 的單個類型形參化為形參。從右至左讀函數的名字,這個函數是 QueueItem 類的構造函數,QueueItem 類嵌套在Queue<Type> 類的作用域中。
如果 QueueItem 類聲明了一個靜態成員,它的定義也需要放在外層作用域中。假定 QueueItem 類有一個靜態成員,它的定義看起來可能像下面這樣:
template <class Type> int Queue<Type>::QueueItem::static_mem = 1024;
實例化外圍類模板的時候,不會自動實例化類模板的嵌套類。像任何成員函數一樣,只有當在需要完整類類型的情況下使用嵌套類本身的時候,才會實例化嵌套類。例如,像Queue<int> qi; 這樣的定義,用 int 類型實例化了 Queue 模板,但沒有實例化QueueItem<int> 類型。成員 head 和 tail 是指向 QueueItem<int> 指針,這裏不需要實例化 QueueItem<int> 來定義那個類的指針。只有當 Queue<int> 類的成員函數中對 head 和 tail 解引用的時候,才實例化 Queue<int> 類。
2:名字查找
對嵌套類中所用名字的名字查找在普通類的名字查找之前進行,現在唯一的區別是可能要查找一個或多個外圍類作用域。
作為嵌套類中名字查找的例子,考慮下面的類聲明:
class Outer { public: struct Inner { // ok: reference to incomplete class void process(const Outer&); Inner2 val; // error: Outer::Inner2 not in scope }; class Inner2 { public: // ok: Inner2::val used in definition Inner2(int i = 0): val(i) { } // ok: definition of process compiled after enclosing class is complete void process(const Outer &out) { out.handle(); } private: int val; }; void handle() const; // member of class Outer };
編譯器首先處理 Outer 類成員的聲明 Outer::Inner 和 Outer::Inner2。
將名字 Outer 作為 Inner::process 形參的使用被綁定到外圍類,在看到process 的聲明時,那個類仍是不完整的,但形參是一個引用,所以這個使用是正確的。
數據成員 Inner::val 的聲明是錯誤的,還沒有看到 Inner2 類型。
Inner2 中的聲明看來沒有問題——它們大多只使用內置類型 int。唯一的例外是成員函數 process,它的形參確定為不完全類型 Outer。因為其形參是一個引用,所以 Outer 為不完全類型是無關緊要的。
直到看到了外圍類中的其余聲明之後,編譯器才處理構造函數和 process成員的定義。
當編譯器查找 Inner2 類中的定義所用的名字時,Inner2 類和 Outer 類中的所有名字都在作用域中。val 的使用(出現在 val 的聲明之前)是正確的:
將該引用綁定到 Inner2 類中的數據成員。同樣,Inner2::process 成員函數體中對 Outer 類的 handle 的使用也正確,當編譯 Inner2 類的成員的時候,整個 Outer 類在作用域中。
C++嵌套類