嵌套类
在C++中, 可以将类声明放在另一个类中. 在另一个类中声明的类被称为嵌套类(nested class), 它通过提供新的类型类作用域来避免名称混乱. 包含类的成原函数可以创建和使用被嵌套类的对象; 而仅当声明位于共有部分, 才能在包含类的外面使用嵌套类, 而且必须使用作用域解析运算符.
对类进行嵌套和包含并不同, 包含意味着将类对象作为另一个类的成员, 而对类进行嵌套不创建类成员, 而是定义了一种类型, 该类型仅在包含嵌套类声明的类中有效.
们先看一个之前的一个例子, 自定义实现的简化版的队列:
class Queue
{
private:
// 这里Node是一个嵌套的结构体定义
// Item是一个别名, 详见之前的笔记
struct Node {Item item; struct Node * next;}
...
};
由于结构是一种其成员在默认情况下为共有的类, 所以Node实际上是一个嵌套类.
在之前的笔记中创建Node对象的方法是这么写的:
bool Queue::enqueue(const Item & item)
{
if(isfull())
return false;
Node * add = new Node;
add->item = item;
add->next = NULL:
...
}
现在我们将Node由struct改为嵌套类:
class Queue
{
// 嵌套类
class Node
{
public:
Item item;
Node * next;
// 构造函数, 将item赋值为i, next指针设置为0, 也就是空值指针
Node(const Item & i): item(i), next(0) {}
};
...
};
然后, 重新使用构造函数编写enqueue():
bool Queue::enqueue(const Item & item)
{
if(isfull())
return false;
Node * add = new Node(item);
...
}
这个例子中是在类声明中定义了构造函数, 假设想在方法文件中定义构造函数, 则定义必须指出Node类是在Queue类中定义的, 我们可以通过两次作用域解析运算符来完成:
Queue::Node::Node(const Item & i): item(i), next(0) {}
嵌套类和访问权限:
首先.嵌套类的声明位置决定了嵌套类的作用域, 即它决定了程序的那些部分可以创建这种类的对象.
其次.嵌套类的共有部分, 保护部分和私有部分控制了对类成员的访问.
1.作用域
如果嵌套类是在另一个类的私有部分声明的, 则只有后者知道它. 前面的例子中Node就是这种情况, 虽然Node没有明确写是private的, 但是默认就是private的. 因此Queue成员可以使用Node对象和指向Node对象的指针, 但是程序的其他部分不知道存在Node类, 对于从Queue派生而来的类, Node也是不可见的, 因为派生类不能直接访问基类的私有部分.
如果嵌套类是在另一个类的保护部分声明的, 则它对于后者可见, 但是对于外部世界是不可见的. 但是这种情况中, 派生类是知道嵌套类的, 并可以直接创建这种类型的对象.
如果嵌套类是在另一个类的共有部分声明的, 则允许后者, 后者的派生类以及外部世界使用它, 因为它是共有的. 然而, 由于嵌套类的作用域为包含它的类, 因此在外部世界使用它时, 必须使用类限定符.
假设有如下的声明:
class Team
{
public:
class Coach {...};
...
};
如果要在Team外面创建Coach对象, 可以这么写:
Team::Coach forhire;
2.访问控制
对嵌套类的访问控制规则与常规类相同. 在Queue类声明中声明Node类并没有赋予Queue类的访问特权, 也没有赋予Node类任何对Queue类的访问特权. 因此Queue类对象只能显示的访问Node对象的共有成员.
类声明的位置决定了类的作用域或可见性, 类可见后, 访问控制规则(共有, 保护, 私有, 友元)将决定程序对嵌套类成员的访问权限.
模板中的嵌套
模板类中同样可以使用嵌套类
// queuetp.h
#ifndef QUEUETP_H_
#define QUEUETP_H_
// 模板类
template <class Item>
class QueueTP
{
private:
enum {Q_SIZE = 10};
// 定义一个嵌套类
class Node
{
public:
Item item;
Node * next;
Node(const Item & i):item(i), next(0){}
};
Node * front;
Node * rear;
int items;
const int qsize;
QueueTP(const QueueTP & q): qsize(0) {}
QueueTP & operator=(const QueueTP & q) {return *this;}
public:
QueueTP(int qs = Q_SIZE);
~QueueTP();
bool isempty() const
{
return items == 0;
}
bool isfull() const
{
return items == qsize;
}
bool queuecount() const
{
return items;
}
bool enqueue(const Item & item);
bool dequeue(Item & item);
};
template<class Item>
QueueTP<Item>::QueueTP(int qs):qsize(qs)
{
// 置为空值指针
front = rear = 0;
items = 0;
}
template<class Item>
QueueTP<Item>::~QueueTP()
{
Node * temp;
while(front != 0)
{
temp = front;
front = front->next;
delete temp;
}
}
template <class Item>
bool QueueTP<Item>::enqueue(const Item & item)
{
if(isfull())
return false;
Node * add = new Node(item);
items++;
if(front == 0)
{
front = add;
} else {
rear->next = add;
}
rear = add;
return true;
}
template<class Item>
bool QueueTP<Item>::dequeue(Item & item)
{
if(front == 0)
return false;
item = front->item;
items--;
Node * temp = front;
front = front->next;
delete temp;
if(items == 0)
rear = 0;
return true;
}
#endif
第二个文件:
// 测试类
// nested.cpp
#include <iostream>
#include <string>
#include "queuetp.h"
int main()
{
using std::string;
using std::cin;
using std::cout;
using std::endl;
QueueTP<string> cs(5);
string temp;
while(!cs.isfull())
{
cout << "Please enter your name. You will be served in the order of arrival. name:" << endl;
getline(cin, temp);
cs.enqueue(temp);
}
cout << "The queue is full. Processing begins!" << endl;
while(!cs.isempty())
{
cs.dequeue(temp);
cout << "Now Processing " << temp << "..." << endl;
}
return 0;
}
程序运行结果为: