C++ Primer 学习笔记_104_特殊工具与技术 --嵌套类

特殊工具与技术

--嵌套类

可以在另一个类内部(与后面所讲述的局部类不同,嵌套类是在类内部)定义一个类,这样的类是嵌套类,也称为嵌套类型。嵌套类最常用于定义执行类.

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

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

嵌套类可以具有与非嵌套类相同种类的成员。像任何其他类一样,嵌套类使用访问标号控制对自己成员的访问。成员可以声明为 public、private 或 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) {}

因为Queue和QueueItem是模板,因此该构造函数也为模板.

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);
        }

C++ Primer 学习笔记_104_特殊工具与技术 --嵌套类,布布扣,bubuko.com

时间: 2024-10-24 04:47:51

C++ Primer 学习笔记_104_特殊工具与技术 --嵌套类的相关文章

C++ Primer 学习笔记_98_特殊工具与技术 --优化内存分配

特殊工具与技术 --优化内存分配 引言: C++的内存分配是一种类型化操作:new为特定类型分配内存,并在新分配的内存中构造该类型的一个对象.new表达式自动运行合适的构造函数来初始化每个动态分配的类类型对象. new基于每个对象分配内存的事实可能会对某些类强加不可接受的运行时开销,这样的类可能需要使用用户级的类类型对象分配能够更快一些.这样的类使用的通用策略是,预先分配用于创建新对象的内存,需要时在预先分配的内存中构造每个新对象. 另外一些类希望按最小尺寸为自己的数据成员分配需要的内存.例如,

C++ Primer 学习笔记_102_特殊工具与技术 --运行时类型识别[续]

特殊工具与技术 --运行时类型识别[续] 三.RTTI的使用 当比较两个派生类对象的时候,我们希望比较可能特定于派生类的数据成员.如果形参是基类引用,就只能比较基类中出现的成员,我们不能访问在派生类中但不在基类中出现的成员. 因此我们可以使用RTTI,在试图比较不同类型的对象时返回假(false). 我们将定义单个相等操作符.每个类定义一个虚函数 equal,该函数首先将操作数强制转换为正确的类型.如果转换成功,就进行真正的比较:如果转换失败,equal 操作就返回 false. 1.类层次 c

C++ Primer 学习笔记_99_特殊工具与技术 --优化内存分配[续1]

特殊工具与技术 --优化内存分配[续1] 三.operator new函数和operator delete 函数 – 分配但不初始化内存 首先,需要对new和delete表达式怎样工作有更多的理解.当使用new表达式 string *sp = new string("initialized"); 的时候,实际上发生三个步骤: 1)首先,表达式调用名为operator new 的标准库函数,分配足够大的原始的未类型化的内存,以保存指定类型的一个对象; 2)接下来,运行该类型的一个构造函数

C++ Primer 学习笔记_105_特殊工具与技术 --联合:节省空间的类

特殊工具与技术 --联合:节省空间的类 联合是一种特殊的类.一个 union 对象可以有多个数据成员,但在任何时刻,只有一个成员可以有值.当将一个值赋给 union 对象的一个成员的时候,其他所有都变为未定义的. 为 union 对象分配的存储的量至少与包含其最大数据成员的一样多.联合提供了便利的办法表示一组相互排斥的值,这些值可以是不同类型的. 1.定义联合 作为例子,我们可能有一个处理不同各类数值或字符数据的过程.该过程可以定义一个 union 来保存这些值: union ToKenValu

C++ Primer 学习笔记_106_特殊工具与技术 --局部类

特殊工具与技术 --局部类 可以在函数体内部定义类,这样的类称为局部类.一个局部类定义了一个类型,该类型只在定义它的局部作用域中可见.与嵌套类不同,局部类的成员是严格受限的. 局部类的所有成员(包括函数)必须完全定义在类定义体内部,因此,局部类远不如嵌套类有用. 实际上,成员完全定义在类中的要求限制了局部类成员函数的复杂性.局部类中的函数很少超过数行代码,超过的话,阅读者会难以理解代码. 类似地,不允许局部类声明 static 数据成员,没有办法定义它们. 1.局部类不能使用函数作用域中的变量

C++ Primer 学习笔记_107_特殊工具与技术 --固有的不可移植的特征[上]

特殊工具与技术 --固有的不可移植的特征[上] C++从 C 语言继承来的不可移植特征:位域和 volatile 限定符.这些特征可使与硬件接口的直接通信更容易. C++ 还增加了另一个不可移植特征(从 C 语言继承来的):链接指示,它使得可以链接到用其他语言编写的程序. 一.位域 可以声明一种特殊的类数据成员,称为位域,来保存特定的位数.当程序需要将二进制数据传递给另一程序或硬件设备的时候,通常使用位域. 位域在内存中的布局是机器相关的. 位域必须是整型数据类型,可以是 signed 或 un

C++ Primer 学习笔记_100_特殊工具与技术 --优化内存分配[续2]

特殊工具与技术 --优化内存分配[续2] 七.一个内存分配器基类 预先分配一块原始内存来保存未构造的对象,创建新元素的时候,可以在一个预先分配的对象中构造:释放元素的时候,将它们放回预先分配对象的块中,而不是将内存实际返还给系统.这种策略常被称为维持一个自由列表.可以将自由列表实现为已分配但未构造的对象的链表. 我们将定义一个名为 CachedObj 的新类来处理自由列表.像 QueueItem 这样希望优化其对象分配的类可以使用 CachedObj 类,而不用直接实现自己的 new 和 del

C++ Primer 学习笔记_103_特殊工具与技术 --类成员指针

特殊工具与技术 --类成员指针 成员指针可以做到:获得特定成员的指针,然后从一个对象或别的对象获得该成员.成员指针应该包含类的类型以及成员的类型. 一.声明成员指针 测试类: class Screen { public: typedef std::string::size_type index; char get() const; char get(index ht,index wd) const; private: std::string contents; index cursor; ind

C++ Primer 学习笔记_101_特殊工具与技术 --运行时类型识别

h2.western { font-family: "Liberation Sans",sans-serif; font-size: 16pt; }h2.cjk { font-family: "微软雅黑"; font-size: 16pt; }h2.ctl { font-family: "AR PL UMing CN"; font-size: 16pt; }h1 { margin-bottom: 0.21cm; }h1.western { fon