在C++中,可以将类声明放在另一个类中。
在另一个类中声明的类,被称为 嵌套类(nested class) 。
它通过提供新的类型类作用域类避免名称混乱。
包含类的成员函数,可以创建和使用被嵌套类的对象。
而仅当声明位于公有部分时,才能在包含类的外面使用嵌套类。而且必须使用作用域解析运算符(旧版本可能不允许使用嵌套类,或无法完全实现这种概念)。
不懂,感觉是一个类A的类声明在另外一个类B里(此时,这个A类的声明,即可能在B的公有部分,也可能在私有部分,还可能在保护部分。),只有当类A的声明在类B的公有部分,类A才能在类B外面使用(即类似数据成员位于公有部分)。并且要加作用域解析运算符才能调用A。
有点类似在类中声明一个结构。
对类进行嵌套,与包含并不相同。包含意味着将类对象作为另一个类的成员(但需要在类定义里面进行声明,并且声明几个就是几个,除非使用new来分配,但这需要一个指针)。而对类进行嵌套不创建类成员,而是定义了一种类型。该类型仅在包含嵌套类声明的类中有效(也就是说,只能在该类方法中声明此类)。
嵌套和包含的区别:
从以上说明来看:
包含一般是固定数量的类对象,或者使用new来动态分配,但就算使用new来动态分配,其指针的数量是一定的(在类定义时确认)。但由于在每个类方法中都可以使用该指针,因此在不同类方法中,可以共享同一个数据。
嵌套,可以自由在类方法中声明类对象,如果该类是自动变量的话,那么离开该类方法时,会自动销毁(自动变量的特性)。因此,一个类方法中声明的嵌套类的对象,不能在另一个类方法中使用(因为其作用域是该类方法)。
假如在一个类中声明另一个类,文中使用的是队列模拟(特点是队首删除,队尾添加)。之前使用的是结构形式(一个成员为项目,即队列里的东西,另一个成员为指针,指向下一个结构的位置)
现就将结构改为类的形式,进行推断:
①首先,数据成员保持不变,即一个数据成员为项目,另一个数据成员为指针。但此时,指针的类型更改,变为该类的指针;
②当添加新对象时(队尾添加),则需要创建一个新的项目,因此需要通过类对象来创建。
由于项目在类中,因此可以调用类的构造函数,在创建类对象的时候,同时创建该项目。又因为项目类型不定(比如说可能是某个类),因此,在构造函数中使用成员初始化列表。(这样,假如项目是类,将自动调用其对应构造函数)。
因此,我们需要自定义类的构造函数。
另外,这个构造函数,要能将指针自动指向空指针(因为新加入的便是最后一个)
③其他变化不大,每进来一个,则要new一个新的(并且赋值)。每结束一个,则调用delete(与new对应)
代码如下(用之前的ATM模拟测试):
#include<iostream> #include<ctime> class Customer { long arrive; //到达时间 int processtime; //自身消耗时间 public: Customer() { arrive = processtime = 0; } //默认构造函数,初始化 long when()const { return arrive; } //返回到达时间 int ptime()const { return processtime; } //返回消耗时间 void set(long when) //将到达时间作为参数传递 { processtime = std::rand() % 3 + 1; //等待1~4单位时间 arrive = when; //到达时间等于when } }; typedef Customer Item; class Queue { private: //先声明一个类 class Node { public: Item item; Node* next; Node(const Item&it) :item(it) { next = NULL; } //默认为空指针,将项目传递给私有数据成员 }; int items = 0; //默认情况下,size(当前项目数)为0 const int MAX; //最大容纳 Node *front; //指向第一个 Node *rear; //指向最后一个 public: Queue(int m = 10); ~Queue(); bool isFull()const { return items == MAX; } //如果等于最大则满 bool isEmpty()const { return items == 0; } //如果等于0则空 int QueueCount() { return items; } //返回数目 bool enqueue(const Item&it); //放入 bool dequeue(Item&it); //取出 }; Queue::Queue(int m) :MAX(m) //构造函数,指针默认为空指针,默认情况下,MAX的值为10 { front = NULL; rear = NULL; } Queue::~Queue() { while (front != NULL) //如果第一个指针指向的不是空(说明不是最后一个 { Node*temp = front; //临时指针 front = front->next; //front指向下一个 delete temp; //删除当前对象(new出来的) } } bool Queue::enqueue(const Item&it) { if (isFull())return false; Node*add = new Node(it); //new一个动态对象(否则就成了自动变量了) if (front == NULL) //如果front是空,说明这是加入的第一个,否则front必然指向第一个(那就不是空) rear = front = add; //如果是新对象,那么rear和front都应该指向当前对象 else //否则加入的不是第一个 { rear->next = add; //上一个Node的指针指向新创建的对象(最后一个) rear = add; //指向最后一个的指针,指向新创建的 } items++; return true; } bool Queue::dequeue(Item&it) { if (isEmpty())return false; Node*temp = front; front = front->next; //指向第一个的指针,指向下一个对象(删除后的第一个) delete temp; //删除原来的第一个对象 if (front == NULL) //如果front是空指针(说明刚刚删除的是最后一个对象) rear = front; //那么rear也应该是空指针 items--; return true; } const int MIN_PER_HR = 60; bool newcustomer(double x); int main() { using namespace std; srand(time(0)); cout << "Case Study: Bank of Heather Automatic Teller\n"; cout << "Enter maximum size of queue: "; int qs; cin >> qs; Queue line(qs); cout << "Enter the number of simulation hours: "; int hours; cin >> hours; long cyclelimit = MIN_PER_HR*hours; cout << "Enter the average number of customer per hour: "; double perhour; cin >> perhour; double min_per_cust; min_per_cust = MIN_PER_HR / perhour; Item temp; long turnaways = 0; long customers = 0; long served = 0; long sum_line = 0; int wait_time = 0; long line_wait = 0; for (int cycle = 0; cycle < cyclelimit; cycle++) { if (newcustomer(min_per_cust)) { if (line.isFull()) turnaways++; else { customers++; temp.set(cycle); line.enqueue(temp); } } if (wait_time <= 0 && !line.isEmpty()) { line.dequeue(temp); wait_time = temp.ptime(); line_wait += cycle - temp.when(); served++; } if (wait_time > 0) wait_time--; sum_line += line.QueueCount(); } if (customers > 0) { cout << "customers accepted: " << customers << endl; cout << " customers served: " << served << endl; cout << " turnaways: " << turnaways << endl; cout << "average queue size: "; cout.precision(2); cout.setf(ios_base::fixed, ios_base::floatfield); cout << (double)sum_line / cyclelimit << endl; cout << " average wait time: " << (double)line_wait / served << " minutes\n"; } else cout << "No customers!\n"; cout << "Done!\n"; system("pause"); return 0; } bool newcustomer(double x) { return (std::rand()*x / RAND_MAX < 1); }
测试结果与之前相同,说明代码正常运行。
调用嵌套类中的方法:
假如Node是一个普通的类,当我们调用它的方法时,是这样做的:Node::isFull();
而此时,Node类在Queue类中,因此,我们调用Node类方法时,则不能像之前那样,但方法类似,加上新的作用域解析运算符,即:Queue::Node::isFull();即可。(前提是,Node类在Queue类的public部分声明)
同样,假如需要在类外声明一个在类内public区域声明的类,需要在类型名前加上作用域解析运算符。例如Queue::Node one; 这样。
但在private和protected区域声明的类,则无法做到这点(即无法在类外使用)。
protected可以在派生类使用,而private只能在当前类使用。
将嵌套类放在模板类之中:
将嵌套类放在模板类之中,很简单,和使用typedef类似。
即使用模板类的<class XX>中的XX作为其类型名。
例如Node类的声明不变(其项目类型为Item),修改Queue类声明:
template<classItem>
class Queue
{
classNode
{
Item item;
...
};
....
};
之后,按照使用模板类的方法,逐个修改Queue类的类方法的函数定义即可。