首页 > 代码库 > C++嵌套类

C++嵌套类

可以在另一个类内部定义一个类,这样的类是嵌套类,也称为嵌套类型。嵌套类是独立的类,基本上与它们的外围类不相关,外围类对嵌套类的成员没有特殊访问权,并且嵌套类对其外围类的成员也没有特殊访问权。

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

嵌套类定义了其外围类中的一个类型成员。像任何其他成员一样,外围类决定对这个类型的访问。在外围类的 public 部分定义的嵌套类定义了可在任何地方使用的类型,在外围类的 protected 部分定义的嵌套类定义了只能由外围类、友元或派生类访问的类型,在外围类的 private 部分定义的嵌套类定义了只能被外围类或其友元访问的类型。

 

1:例子

template <class Type> class Queue {
public:
    ... 
private:
    // public members are ok: QueueItem is a private member of Queue
    // only Queue and its friends may access the members of QueueItem
    struct QueueItem {
        QueueItem(const Type &);
        Type item; 
        QueueItem *next; 
    };
    QueueItem *head; 
    QueueItem *tail;
};

因为 QueueItem 类是 private 成员,所以只有 Queue 类的成员和友元可以使用 QueueItem 类型。使 QueueItem 类成为 private 成员之后,就可以使QueueItem 成员 public。

 

因为 Queue 类是一个模板,它的成员也隐含地是模板。具体而言,嵌套类QueueItem 隐含地是一个类模板。像 Queue 类中任何其他成员一样,QueueItem的模板形参与其外围类(Queue 类)的模板形参相同。

 

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

成员的名字在类外部是不可见的。要定义QueueItem的构造函数,必须指出,QueueItem 是 Queue 类作用域中的嵌套类:

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

这段代码定义了一个函数模板,以名为 Type 的单个类型形参化为形参。从右至左读函数的名字,这个函数是 QueueItem 类的构造函数,QueueItem 类嵌套在Queue<Type> 类的作用域中。

 

如果 QueueItem 类声明了一个静态成员,它的定义也需要放在外层作用域中。假定 QueueItem 类有一个静态成员,它的定义看起来可能像下面这样:

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

 

实例化外围类模板的时候,不会自动实例化类模板的嵌套类。像任何成员函数一样,只有当在需要完整类类型的情况下使用嵌套类本身的时候,才会实例化嵌套类。例如,像Queue<int> qi; 这样的定义,用 int 类型实例化了 Queue 模板,但没有实例化QueueItem<int> 类型。成员 head 和 tail 是指向 QueueItem<int> 指针,这里不需要实例化 QueueItem<int> 来定义那个类的指针。只有当 Queue<int> 类的成员函数中对 head 和 tail 解引用的时候,才实例化 Queue<int> 类。

 

2:名字查找

对嵌套类中所用名字的名字查找在普通类的名字查找之前进行,现在唯一的区别是可能要查找一个或多个外围类作用域。

作为嵌套类中名字查找的例子,考虑下面的类声明:

class Outer {
public:
    struct Inner {
        // ok: reference to incomplete class
        void process(const Outer&);
        Inner2 val; // error: Outer::Inner2 not in scope
    };
    
    class Inner2 {
    public:
        // ok: Inner2::val used in definition
        Inner2(int i = 0): val(i) { }
        // ok: definition of process compiled after enclosing class is complete
        void process(const Outer &out) { out.handle(); }
    private:
        int val;
    };
    
    void handle() const; // member of class Outer
};

编译器首先处理 Outer 类成员的声明 Outer::Inner 和 Outer::Inner2。

将名字 Outer 作为 Inner::process 形参的使用被绑定到外围类,在看到process 的声明时,那个类仍是不完整的,但形参是一个引用,所以这个使用是正确的。

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

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

直到看到了外围类中的其余声明之后,编译器才处理构造函数和 process成员的定义。

当编译器查找 Inner2 类中的定义所用的名字时,Inner2 类和 Outer 类中的所有名字都在作用域中。val 的使用(出现在 val 的声明之前)是正确的:

将该引用绑定到 Inner2 类中的数据成员。同样,Inner2::process 成员函数体中对 Outer 类的 handle 的使用也正确,当编译 Inner2 类的成员的时候,整个 Outer 类在作用域中。

C++嵌套类