zoukankan      html  css  js  c++  java
  • C++ Primer 学习笔记_104_特殊工具与技术 --嵌套类

    特殊工具与技术

    --嵌套类

    能够在还有一个类内部(与后面所讲述的局部类不同,嵌套类是在类内部)定义一个类,这种类是嵌套类,也称为嵌套类型。嵌套类最经常使用于定义运行类.

    嵌套类是独立的类,基本上与它们的外围类不相关,因此,外围类和嵌套类的对象是互相独立的。嵌套类型的对象不具备外围类所定义的成员,相同,外围类的成员也不具备嵌套类所定义的成员。 

    嵌套类的名字在其外围类的作用域中可见,但在其它类作用域或定义外围类的作用域中不可见。嵌套类的名字将不会与还有一作用域中声明的名字冲突

    嵌套类能够具有与非嵌套类相同种类的成员。像不论什么其它类一样,嵌套类使用訪问标号控制对自己成员的訪问。成员能够声明为 publicprivate 或 protected。外围类对嵌套类的成员没有特殊訪问权,并且嵌套类对其外围类的成员也没有特殊訪问权。 

    嵌套类定义了其外围类中的一个类型成员。像不论什么其它成员一样,外围类决定对这个类型的訪问。

    嵌套类的实现

    [实例]

    将 QueueItem 类设为 Queue 类的 private 成员,那样,Queue 类(及其友元)能够使用 QueueItem,但 QueueItem 类类型对普通用户代码不可见。一旦 QueueItem 类本身为 private,我们就能够使其成员为 public 成员--仅仅有 Queue 或 Queue 的友元能够訪问 QueueItem 类型,所以不必防止一般程序訪问 QueueItem 成员。通过用保留字 struct 定义 QueueItem 使成员为 public 成员。 

    新的设计例如以下:

    template <typename Type>
    class Queue
    {
    public:
        //...
    
    private:
        struct QueueItem
        {
            QueueItem(const Type &);
            Type item;
            QueueItem *next;
        };
    
        QueueItem *head;
        QueueItem *tail;
    };

    1.嵌套在类模板内部的类是模板

    由于Queue是模板,因此它的成员也是模板,并且QueueItem的模板形參与其外围类Queue的模板形參相同.

    Queue 类的每次实例化用相应于 Type 的适当模板实參产生自己的QueueItem 类。QueueItem 类模板的实例化与外围 Queue 类模板的实例化之间的映射是一对一的

    2.定义嵌套类的成员

    在其类外部定义的嵌套类成员,必须定义在定义外围类的同一作用域中。在其类外部定义的嵌套类的成员,不能定义在外围类内部,嵌套类的成员不是外围类的成员。 

    QueueItem 类的构造函数不是 Queue 类的成员,因此,不能将它定义在Queue 类定义体中的不论什么地方.

    template <class Type>
    Queue<Type>::QueueItem::QueueItem(const Type &t):
    item(t),next(0) {}

    由于QueueQueueItem是模板,因此该构造函数也为模板.

    3.在外围类外部定义嵌套类

    嵌套类通常支持外围类的实现细节。我们可能希望防止外围类的用户看见嵌套类的实现代码。 

    比如,我们可能希望将 QueueItem 类的定义放在它自己的文件里,我们能够在 Queue 类及其成员的实现文件里包括这个文件。正如能够在类定义体外部定义嵌套类的成员一样,我们也能够在外围类定义体的外部定义整个嵌套类:

    template <typename Type>
    class Queue
    {
    public:
        //...
    
    private:
        struct QueueItem;
    
        QueueItem *head;
        QueueItem *tail;
    };
    
    template <class Type>
    struct Queue<Type>::QueueItem
    {
        QueueItem(const Type &t):item(t),next(0) {}
    
        Type item;
        QueueItem *next;
    };

    注意,我们必须在Queue类体内部声明QueueItem.

    [小心]

    在看到在类定义体外部定义的嵌套类的实际定义之前,该类是不全然类型,应用全部使用不全然类型的常规限制。

    4.嵌套类静态成员定义

    假设QueueItem类声明了一个静态成员,它的定义也须要放在外层作用域中,假定QueueItem有一个静态成员:

    template <class Type>
    int Queue<Type>::QueueItem::static_mem = 1024;

    5.使用外围类的成员

    外围作用域的对象与其嵌套类型的对象之间没有联系

    template <typename Type>
    void Queue<Type>::pop()
    {
        QueueItem *q = head;
        head = head -> next;
        delete q;
    }

    Queue 类型的对象没有名为 item 或 next 成员Queue 类的函数成员能够使用 head 和 tail 成员(它们是指向 QueueItem 对象的指针)来获取那些QueueItem 成员。

    6.使用静态成员或其它类型的成员

    嵌套类能够直接引用外围类的静态成员,类型名枚举成员[同后面所讲的局部类].然而,引用外围类作用域之外的类型名或静态成员,须要作用域确定操作符.

    7.嵌套模板的实例化

    实例化外围类模板的时候,不会自己主动实例化类模板的嵌套类。像不论什么成员函数一样,仅仅有当在须要完整类类型的情况下使用嵌套类本身的时候,才会实例化嵌套类。比如,像 

    Queue<int> qi; 


    这种定义,用 int 类型实例化了 Queue 模板,但没有实例化QueueItem<int> 类型。成员 head 和 tail 是指向 QueueItem<int> 指针,这里不须要实例化 QueueItem<int> 来定义那个类的指针。 

    仅仅有在使用 QueueItem<int> 的时候--仅仅有当 Queue<int> 类的成员函数中对 head 和 tail 解引用的时候,才实例化 QueueItem<int> 类。

    嵌套类作用域中的名字查找

    当处理类成员声明的时候,所用的名字必须在使用之前出现;当处理定义的时候,整个嵌套类和外围类均在作用域中.

    class Outer
    {
    public:
        struct Inner
        {
            void process(const Outer &);    //OK
            Inner2 val;     //Error
        };
        struct Inner2
        {
        public:
            Inner2(int i = 0):val(i) {}
    
            void process(const Outer &out)  //OK
            {
                out.handle();
            }
    
        private:
            int val;
        };
    
        void handle() const;
    };

    [说明]

    编译器首先处理 Outer 类成员的声明 Outer::Inner 和 Outer::Inner2。将名字 Outer 作为 Inner::process 形參的使用被绑定到外围类,在看到process 的声明时,那个类仍是不完整的,但形參是一个引用,所以这个使用是正确的。 

    数据成员 Inner::val 的声明是错误的,还没有看到 Inner2 类型。 

    Inner2 中的声明看来没有问题--它们大多仅仅使用内置类型 int。唯一的例外是成员函数 process,它的形參确定为不全然类型 Outer。由于其形參是一个引用,所以 Outer 为不全然类型是无关紧要的。 

    直到看到了外围类中的其余声明之后,编译器才处理构造函数和 process 成员的定义。对 Outer 类声明的完毕将函数 handle 的声明放在作用域中。 

    当编译器查找 Inner2 类中的定义所用的名字时,Inner2 类和 Outer 类中的全部名字都在作用域中。val 的使用(出如今 val 的声明之前)是正确的:将该引用绑定到 Inner2 类中的数据成员[不大理解这一段是什么意思%>_<%]。相同,Inner2::process 成员函数体中对 Outer 类的 handle 的使用也正确,当编译 Inner2 类的成员的时候,整个 Outer 类在作用域中

    使用作用域操作符控制名字查找

    能够使用作用域操作符訪问handle的全局版本号:

            void process(const Outer &out)
            {
                ::hadle(out);
            }

  • 相关阅读:
    字符串匹配的KMP算法(转)
    二分查找谜题
    快排的优化--说说尾递归
    ZR提高失恋测4
    CF1209
    ZR普转提2
    ZR提高失恋测3
    ZR提高失恋测2(9.7)
    ZR9.8普转提
    CF1214
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/4509125.html
Copyright © 2011-2022 走看看