在类中可以再定义类,称为嵌套类或内部类,应用的场景之一是实现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
,这样就没有变更的问题了。