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

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

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

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

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

 class Queue

 {

 private:

 //classs scope definitions

   //Node is a nested structure definition local to this class

   struct node {Item item; struct Node * next;};

 }

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

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

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

Queue的方法enqueue创建了Node:

 bool Queue::enqueue(const Item & item)
{
if(isfull())
return false;
Node * add = new Node; //create node
//on failure,new throws std::bad_alloc exception
add->item =item; //set node pointer
add->next =NULL;
...
}

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

于是可以改写Node如下:

 class Queue
{
//Node is a nested calss definition local to this class
class Node
{
public:
Item item;
Node * next;
Node(const Item & i):item(i),next() { }
};
...
}

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

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

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

这使得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类定义转换为模板时,是否会由于它包含嵌套类而带来问题?答案是不会。

 //queuetp.h -- queue template with a nested class
#ifndef QUEUETP_H_
#define QUEUETP_H_ template <class Item>
class QueueTP
{
private:
enum {Q_SIZE =};
//Node is a nested class definition
class Node
{
public:
Item item;
Node * next;
Node(const Item & i): item(i),next() {}
};
Node * front;
Node * rear;
int item; //current number of items in Queue;
const int qsize; //maximum number of items in Queue;
QueueTP(const QueueTP & q): qsize() {}
QueueTP & operator=(const QueueTP & q) {return *this;}
public:
QueueTP(int qs= Q_SIZE);
~QueueTP();
bool isempty() const{
return item==;
};
bool isfull() const{
return item==qsize;
};
int queuecount const{
return item;
};
bool enqueue(const Item & item); //add item to end
bool dequeue(Item & item); //remove item from front
}; #endif
 //QueueTP methods
QueueTP<Item>::QueueTP(int qs): qsize(qs)
{
front = reart = ;
items = ;
} template <class Item>
QueueTP<Item>::~QueueTP()
{
Node * temp;
while()
{
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); //create node
//on failure, new throws std::bad_alloc exception
items++;
if(front ==)
front == add;
else
rear->next == add;
rear = add;
return true;
} template <class Item>
bool QueueTP<Item>::dequeue(Item & item)
{
if(front == )
return false;
item = front->item;
items--;
Node * temp =front;
front = front->next;
delete temp;
if(items==)
rear = ;
return true;
}

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

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

    QueueTP<double> dq;

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

  QueueTP<char> dq;

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

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

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

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

 //nested.cpp  -- using a queue that has a nested class

 #include <iostream>
#include <string>
#include "queuetp.h" int main()
{
using std::string;
using std::cin;
using std::cout; QueueTP<string> cs();
string temp; while(!cs.isfull())
{
cout<<"Please enter your name.Your will be"
"served in the order of arrival.\n"
"name:";
getline(cin,temp);
cs.enqueue(temp);
}
cout<<"The queue is full.Processing begins!\n"; while(!cs.isempty())
{
cs.dequeue(temp);
cout<<"Now processing "<<temp<<"...\n";
}
return ;
}

最新文章

  1. linux系统root用户忘记密码的重置方法
  2. Microsoft Visual Studio正忙解决办法
  3. github打不开
  4. Redis在Windows下的安装和使用
  5. C# gridControl 部分设置
  6. UIKIT的简介
  7. 高质量JavaScript代码书写基本要点
  8. 什么是CC攻击,如何防止网站被CC攻击的方法总汇
  9. PHP第四章数组2
  10. vs2012C#编程环境设置智能提示
  11. jQuery的动态绑定事件的应用
  12. iOS10构建版本不显示的问题
  13. typescript 的 polyfill 学习1-Class 继承篇
  14. Nuxt 自动化部署及打包后文件自动上传七牛云
  15. 跟随我在oracle学习php(10)
  16. Annotation(注解)介绍
  17. node.js中对 redis 的安装和基本操作
  18. 介绍Ajax与jQuery技术
  19. 方程:方程(equation)是指含有未知数的等式
  20. filter运行出现 &lt;filter object at 0x000001B68F052828&gt; 判断素数

热门文章

  1. 深入剖析SolrCloud(四)
  2. Professional C# 6 and .NET Core 1.0 - Chapter 38 Entity Framework Core
  3. php格式化时间戳显示友好的时间
  4. activex打包
  5. 【原创】请不要对Boost Format使用Byte作为参数
  6. Qt工程文件Pro介绍(转)
  7. 第04章-面向切面的Spring
  8. 编写高质量代码改善C#程序的157个建议——建议39:了解委托的实质
  9. window中启动vs后鼠标无法移动
  10. 一个Sql备注