• 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.
    "
    20                    "name:";
    21         getline(cin,temp);
    22         cs.enqueue(temp);
    23     }
    24     cout<<"The queue is full.Processing begins!
    ";
    25 
    26     while(!cs.isempty())
    27     {
    28         cs.dequeue(temp);
    29         cout<<"Now processing "<<temp<<"...
    ";
    30     }
    31     return 0;  
    32 }
  • 相关阅读:
    electron webview加载远程preload方法
    vue 2.0使用笔记
    关于node的setTimeout的延时最大限制
    javascript的未知尺寸图片保持比例水平垂直居中函数
    node的“宏任务(macro-task)”和“微任务(micro-task)”机制
    windows git gui右键sublime/vs code打开当前文件编辑
    一个JavaScript组件都需要哪些基础api
    点击label时click事件被触发两次的坑
    javascript iframe相关操作
    javascript 写了个字符串组合的情况
  • 原文地址:https://www.cnblogs.com/grooovvve/p/10422220.html
Copyright © 2020-2023  润新知