C++_类入门3-嵌套类

可以将类B声明在另一个类中。在另一个类A中声明的类B被称为嵌套类(nested class)

类A的成员函数可以创建和使用嵌套类B的对象。

当且仅当声明为公有部分时,才能在类A的外面使用嵌套类。而且必须使用作用域解析运算符。(旧版C++不支持嵌套类概念)

       对类进行嵌套和包含并不同。包含意味着将类C对象作为类A的成员。而对类B进行嵌套不创建类成员,而是定义了一种类型,该类型仅仅在包含嵌套类声明的类A中有效。

       对类进行嵌套通常是为了帮助实现另一个类,并避免名称冲突。

 1 class Queue
 2 
 3 {
 4 
 5 private:
 6 
 7 //classs scope definitions
 8 
 9   //Node is a nested structure definition local to this class
10 
11   struct node {Item item; struct Node * next;};
12 
13 }

结构是其成员在默认情况下公有的类。结构可以理解成一种特殊的类。

所以Node实际上可以看成嵌套类。但该定义没有充分利用类的功能。

具体来说,它没有显式构造函数。接下来进行补救。

Queue的方法enqueue创建了Node:

 1 bool Queue::enqueue(const Item & item)
 2 {
 3     if(isfull())
 4         return false;
 5     Node * add = new Node;  //create node
 6 //on failure,new throws std::bad_alloc exception
 7     add->item =item;    //set node pointer
 8     add->next =NULL;
 9     ...
10 }

上述代码创建Node后,显式地给Node成员赋值,这种工作更适合由构造函数来完成。

于是可以改写Node如下:

 1 class Queue
 2 {
 3     //Node is a nested calss definition local to this class
 4     class Node
 5     {
 6     public:
 7         Item item;
 8         Node * next;
 9         Node(const Item & i):item(i),next(0) { }
10     };
11     ...  
12 }

该构造函数节点的item成员被初始化为i,并将next指针置为0,这是C++编写空值指针的方法之一。

接下来,需要使用构造函数重新编写enqueue():

1 bool Queue::enqueue(const Item & item)
2 {
3     if(isfull())
4         reutrn false;
5     Node * add = new Node(item);  //create, initialize node
6 //on failure,new throws std::bad_alloc exception
7 ...      
8 }

这使得enqueue()的代码更短,也更安全,因为它自动进行初始化,无需程序员记住应该做什么。

这个例子在类声明中定义了构造函数。假设想在方法文件中定义构造函数,则定义必须指出Node类,

这是通过两次作用域运算符来完成的;

Queue::Node::Node(const Item & i):item(i),next(0) { }

嵌套类和访问权限

嵌套类的声明位置决定了嵌套类的作用域。即它决定了程序的那些部分可以创建这种类的对象。

其次,和其他类一样,嵌套类的公有部分、保护部分、私有部分控制了对类成员的访问。

在哪些地方可以使用嵌套类以及如何使用嵌套类,取决于作用域和访问控制

1、作用域

如果嵌套类是在类的私有部分声明的:

  则只有类的私有部分知道它,在前一个例子中,被嵌套在Queue声明中的Node类就属于这种情况。

  Queue成员可以使用Node对象和Node对象的指针,但是程序的其他部分甚至不知道存在Node类。

  而对于从Queue派生而来的类,Node也是不可见的。因为派生类不能直接访问积累的私有部分。

如果嵌套类是在类的保护部分声明的:

  则该嵌套类对于后面这个类是可见的,但是对于外部世界则是不可见的。

   在这种情况下,派生类知道嵌套类,病可以直接创建这种类型的对象。  

如果嵌套类是在类的公有部分声明的:

  则后者、后者的派生类以及外部世界使用它,因为它是公有的。

  然而,由于嵌套类的作用域为包含它的类,因此在外部世界使用它时,必须使用类限定符。

嵌套结构和枚举的作用域如此相同。其实,很多程序员都使用公有枚举来提供可供客户程序员使用的类常数。

作用域就是指这个对象(类、枚举)可见的范围,有效的范围。

2、访问控制

类可见之后,起决定作用的就是访问控制

类声明的位置决定了类的作用域或可见性。类可见后,访问控制规则(公有、保护、私有、友元)将决定程序对嵌套类成员的访问权限。

在Queue类声明中声明Node并没有赋予Queue类对Node类的访问特权。

因此Queue类对象只能显式地访问Node对象的公有成员。

由于这个原因,在Queue示例中,Node类的所有成员都被声明为公有的。

这样有悖于应将数据成员声明为私有的惯例,但Node类是Queue类内部实现的一项特性,对外部实际不可见,这是因为Queue类是在Queue类的私有部分声明的。

所以,虽然Queue的方法可以直接访问Node成员,但使用Queue类的客户不能这样做。

模板中的嵌套

模板很适合用于诸如实现Queue等容器类。

将Queue类定义转换为模板时,是否会由于它包含嵌套类而带来问题?答案是不会。

 1 //queuetp.h -- queue template with a nested class
 2 #ifndef QUEUETP_H_
 3 #define QUEUETP_H_
 4 
 5 template <class Item>
 6 class QueueTP
 7 {
 8 private:
 9     enum {Q_SIZE =10};
10     //Node is a nested class definition
11     class Node
12     {
13     public:
14         Item item;
15         Node * next;
16         Node(const Item & i): item(i),next(0) {}
17     };
18     Node * front;
19     Node * rear;
20     int item;           //current number of items in Queue;
21     const int qsize;  //maximum number of items in Queue;
22     QueueTP(const QueueTP & q): qsize(0) {}
23     QueueTP & operator=(const QueueTP & q) {return *this;}
24 public:
25     QueueTP(int qs= Q_SIZE);
26     ~QueueTP();
27     bool isempty() const{
28         return item==0;
29     };
30     bool isfull() const{
31         return item==qsize;
32     };
33     int queuecount const{
34         return item;
35     };
36     bool enqueue(const Item & item);  //add item to end
37     bool dequeue(Item & item);   //remove item from front
38 };
39 
40 
41 #endif
 1 //QueueTP methods
 2 QueueTP<Item>::QueueTP(int qs): qsize(qs)
 3 {
 4     front = reart = 0;
 5     items = 0;
 6 }
 7 
 8 
 9 template <class Item>
10 QueueTP<Item>::~QueueTP()
11 {
12     Node * temp;
13     while()
14     {
15         temp=front;
16         front=front->next;
17         delete temp;
18     }
19 }
20 
21 
22 template <class Item>
23 bool QueueTP<Item>::enqueue(const Item & item)
24 {
25     if(isfull())
26         return false;
27     Node * add = new Node(item);  //create node
28 //on failure, new throws std::bad_alloc exception
29     items++;
30     if(front ==0)
31         front == add;
32     else
33         rear->next == add;
34     rear = add;
35     return true;
36 }
37 
38 
39 template <class Item>
40 bool QueueTP<Item>::dequeue(Item & item)
41 {
42     if(front == 0)
43         return false;
44     item = front->item;
45     items--;
46     Node * temp =front;
47     front = front->next;
48     delete temp;
49     if(items==0)
50         rear = 0;
51     return true;
52 }

在模板中Node是利用Item类型来定义的。

下面声明将导致Node被定义成用于储存double值:

    QueueTP<double> dq;

而下面的声明将导致Node被定义成用于存储char的值:

  QueueTP<char> dq;

这两个Node类将在两个独立的QueueTP类中定义,因此不会发生名称冲突。

即一个节点的类型为QueueTP<double>::Node

另一个节点的类型为QueueTP<char>::Node

接下来用一个小程序测试一下:

 1 //nested.cpp  -- using a queue that has a nested class
 2 
 3 #include <iostream>
 4 #include <string>
 5 #include "queuetp.h"
 6 
 7 int main()
 8 {
 9     using std::string;
10     using std::cin;
11     using std::cout;
12 
13     QueueTP<string> cs(5);
14     string temp;
15 
16     while(!cs.isfull())
17     {
18         cout<<"Please enter your name.Your will be"
19                    "served in the order of arrival.\n"
20                    "name:";
21         getline(cin,temp);
22         cs.enqueue(temp);
23     }
24     cout<<"The queue is full.Processing begins!\n";
25 
26     while(!cs.isempty())
27     {
28         cs.dequeue(temp);
29         cout<<"Now processing "<<temp<<"...\n";
30     }
31     return 0;  
32 }

猜你喜欢

转载自www.cnblogs.com/grooovvve/p/10422220.html