在類別中可以再定義類別,稱為巢狀類別或內部類別,應用的場景之一是實作 IntLinkedList
時,內部節點可用 IntNode
來定義:
#include <iostream>
using namespace std;
class IntLinkedList {
class IntNode {
public:
IntNode(int value, IntNode *next) : value(value), next(next) {}
int value;
IntNode *next;
};
IntNode *first = nullptr;
public:
IntLinkedList& append(int value);
int get(int i);
};
IntLinkedList& IntLinkedList::append(int value) {
IntNode *node = new IntNode(value, nullptr);
if(first == nullptr) {
this->first = node;
}
else {
IntNode *last = this->first;
while(last->next != nullptr) {
last = last->next;
}
last->next = node;
}
return *this;
}
int IntLinkedList::get(int i) {
IntNode *last = this->first;
int count = 0;
while(true) {
if(count == i) {
return last->value;
}
last = last->next;
count++;
}
}
int main() {
IntLinkedList lt;
lt.append(1).append(2).append(3);
cout << lt.get(1) << endl;
return 0;
}
範例中 append
以 new
的方式建構了 IntNode
實例,應該要有個解構式,在不需要 IntLinkedList
時,將這些動態建立的 IntNode
清除,這在之後的文件再來詳加探討,目前暫時忽略這個議題。
內部類別也可以獨立於外部類別定義,例如:
class IntLinkedList {
class IntNode;
IntNode *first = nullptr;
public:
IntLinkedList& append(int value);
int get(int i);
};
class IntLinkedList::IntNode {
public:
IntNode(int value, IntNode *next) : value(value), next(next) {}
int value;
IntNode *next;
};
在範例中,IntNode
的值域是 public
,這是為了便於給外部類別取用 IntNode
的值域,因為內部類別中若有 private
成員,外部類別預設也是不可存取的。
IntLinkedList
的使用者不需要知道 IntNode
的存在,因此 IntNode
被設定為 IntLinkedList
的 private
成員,這將直接將 IntNode
的值域設為 public
,也只有 IntLinkedList
可以存取。
然而有時候,內部類別會是 public
,你又不想公開某些值域,又想允許外部類別存取內部類別的 private
值域,怎麼辦呢?可以宣告外部類別是內部類別的朋友,例如:
#include <iostream>
#include <string>
using namespace std;
class Screen {
public:
class Pixel {
int x;
int y;
friend Screen; // 朋友類別
public:
Pixel(int x, int y) : x(x), y(y) {}
};
string info(Pixel px) {
return "Pixel(" + to_string(px.x) + ", " + to_string(px.y) + ")";
}
};
int main() {
Screen screen;
Screen::Pixel px(10, 10);
cout << screen.info(px) << endl;
return 0;
}
被 friend
修飾的對象並不是類別成員的一部份,單純是種存取控制,在這個範例中,Screen::Pixel
的值域不希望被公開存取,然而,允許 Screen
存取,當然,允許存取 private
成員,表示之間有強烈的耦合關係,就範例來說,螢幕包含像素資訊,這邊設計為這種的耦合關係是可以允許的。
被 friend
修飾的對象可以是類別、函式或者是另一類別的方法,例如重載運算子時,若選擇以非成員函式實作,就有可能需要將非成員函式設為 friend
,在〈運算子重載〉中就有個例子;然而要記得,允許存取 private
成員,表示之間有強烈的耦合關係,只有在有充分理由之下,才定義哪些該設定為朋友。
類別也可以在定義在函式之中,也就是區域類別,主要用來臨時封裝一組資料,然而,不可以存取函式中的區域變數:
#include <iostream>
using namespace std;
int main() {
class Point {
public:
Point(int x, int y) : x(x), y(y) {}
int x;
int y;
};
Point p1(10, 10);
Point p2(20, 20);
return 0;
}
必要時,區域類別也可以匿名,也就是匿名類別:
#include <iostream>
using namespace std;
int main() {
const int dx = 10;
const int dy = 20;
class {
public:
int x = dx;
int y = dy;
} p;
cout << p.x << endl;
return 0;
}
範例中的 const
是必要的,因為類別中出現的 dx
、dy
實際上並不是外部的 dx
、dy
,編譯器在類別中建立了新的 dx
、dy
,將外部 dx
、dy
的值複製,為了避免類別中試圖參考或取址後進行變更,誤以為外部的 dx
、dy
取值時也會隨之變化,故要求加上 const
,這麼一來類別中試圖參考或取址也得加上 const
,這樣就沒有變更的問題了。