嵌套类
一个类可以定义在另一个类的内部,前者称为嵌套类或嵌套类型。
下面是一个使用C++嵌套类的示例:
#include <iostream>
class OuterClass {
public:
class InnerClass {
public:
void printMessage() {
std::cout << "Hello from InnerClass!" << std::endl;
}
};
void callInnerClass() {
InnerClass inner;
inner.printMessage();
}
};
int main() {
OuterClass outer;
outer.callInnerClass();
return 0;
}
在上面的代码中,OuterClass
有一个嵌套类InnerClass
。在OuterClass
的callInnerClass()
函数中,我们创建了InnerClass
的一个对象,并调用了它的printMessage()
函数。最终的输出结果将会是"Hello from InnerClass!"。
嵌套类的性质
嵌套类常用于定义作为实现部分的类
- 嵌套类是一个独立的类,与外层类基本没什么关系。
- 特别是,外层类的对象和嵌套类的对象是相互独立的。
- 在嵌套类的对象中不包含任何外层类定义的成员;
- 类似的,在外层类的对象中也不包含任何嵌套类定义的成员。
嵌套类的名字在外层类作用域中是可见的,在外层类作用域之外不可见。
class A
{
class B
{
};
B a;//正确
};
B b;//错误
和其他嵌套类的名字不会和别的作用域中的同一个名字冲突。
class B
{
};
class A
{
class B//不冲突
{
};
};
嵌套类中成员的种类与非嵌套类是一样的。
class A
{
class B
{
};
B a;
int b;//a,b都是成员
};
和其他类类似,嵌套类也使用访问限定符的名字一样,来控制外界对其成员的访问权限。
class A
{
class B
{
public:
int b;
};
};
外层类对嵌套类的成员没有特殊的访问权限,同样,嵌套类对外层类的成员也没有特殊的访问权限。
class A
{
public:
int a;
class B
{
public:
int b;
void C()
{
cout << a << endl;//错误,嵌套类对外部类成员没有特殊访问权限
}
};
void D()
{
cout << b << endl;//错误,外部类对嵌套类成员没有特殊访问权限
}
};
嵌套类在其外层类中定义了一个类型成员。和其他成员类似,该类型的访问权限由外层类决定。
- 位于外层类public部分的嵌套类实际上定义了一种可以随处访问的类型;
- 位于外层类protected部分的嵌套类定义的类型只能被外层类及其友元和派生类访问:
- 位于外层类private部分的嵌套类定义的类型只能被外层类的成员和友元访问。
class OuterClass {
public:
class NestedClass {
public:
void doSomething() {
// 嵌套类的成员实现
}
};
NestedClass nestedObj; // 嵌套类对象
void useNestedClassMember() {
nestedObj.doSomething(); // 调用嵌套类的成员
}
};
int main() {
OuterClass outerObj;
outerObj.useNestedClassMember(); // 通过外部类对象调用嵌套类的成员
return 0;
}
声明一个嵌套类
我们举个例子
class A {
public:
class B; //嵌套类稍后定义
//...
};
我们只需对原来的A类做一处改动,即将B声明成嵌套类。
因为B是一个类型成员,所以我们必须对它先声明后使用.
在外层类之外定义一个嵌套类
我们在A内声明了B,但是没有给出它的定义。
和成员函数一样,嵌套类必须声明在类的内部,但是可以定义在类的内部或者外部。
当我们在外层类之外定义一个嵌套类时,必须以外层类的名字限定嵌套类的名字;
class A::B
{
public:
int a;
}
在嵌套类在其外层类之外完成真正的定义之前,它都是一个不完全类型
定义嵌套类的成员
在这个版本的A类中,我们并没有在类的内部定义其构造函数。
要想为其定义构造函数,必须指明B是嵌套在A的作用域之内的。具体做法是使用外层类的名字限定联套类的名字:
A::B::B(int a_):a(a_){}
该构造函数除了把实参值赋给对应的数据成员之外,没有做其他工作。
访问嵌套类的成员
我们访问嵌套类成员只能通过创建嵌套类对象才能访问!!!
class A
{
public:
int a;
class B
{
public:
void C()
{
cout << "使用成功" << endl;
}
};
B b;
void A1()
{
b.C();//通过嵌套类对象使用其中的数据成员
}
};
我们不能直接在外部类里直接访问嵌套类成员
嵌套类的静态成员定义
如果B声明了一个静态成员,则该成员的定义将位于A的作用域之外。
例如,假设B有一个静态成员,则该成员的定义将形如:
int A::B::static_mem= 1024;
嵌套类作用域中的名字查找
名字查找的一般规则在嵌套类中同样适用。
当然,因为嵌套类本身是一个嵌套作用域,所以还必须查找嵌套类的外层作用域。
class A
{
public:
int a;
class B
{
};
B N()//我们可以随意使用B这个名字
{
;
}
};
嵌套类和外层类是相互独立的
尽管嵌套类定义在其外层类的作用域中,但是读者必须谨记外层类的对象和嵌套类的对象没有任何关系。
嵌套类的对象只包含嵌套类定义的成员;
同样,外层类的对象只包含外层类定义的成员,在外层类对象中不会有任何嵌套类的成员。
有的人可能会写出下面这样子的代码
这个程序企图通过一个A类对象来使用B类的成员,但是这是错误的,A类和B类的数据成员没有任何特殊访问权限,它们是相互独立的,要想访问B类的数据成员,只能通过一个B类对象来实现
局部类
类可以定义在某个函数的内部,我们称这样的类为局部类。
局部类定义的类型只在定义它的作用域内可见。
和嵌套类不同,局部类的成员受到严格限制。
局部类的所有成员(包括函数在内)都必须完整定义在类的内部。因此,局部类的作用与嵌套类相比相差很远
在实际编程的过程中,因为局部类的成员必须完整定义在类的内部,所以成员函数的复杂性不可能太高。局部类的成员函数一般只有几行代码,否则我们就很难读懂它了。
类似的,在局部类中也不允许声明静态数据成员,因为我们没法定义这样的成员。
局部类不能使用函数作用域中的变量
局部类对其外层作用域中名字的访问权限受到很多限制,局部类只能访问外层作用域定义的类型名、静态变量以及枚举成员。
如果局部类定义在某个函数内部,则该函数的普通局部变量不能被该局部类使用:
int a, val;
void foo(int val)
{
static int si;
enum Loc { a = 1024, b };
// Bar是foo的局部类
struct Bar {
Loc locVal; // 正确:使用一个局部类型名
int barVal;
void fooBar(Loc l = a) // 正确:默认实参是Loc::a
{
barVal = val; //错误:val是foo的局部变量
barVal = ::val; // 正确:使用一个全局对象
barVal = si; // 正确:使用一个静态局部对象
locVal = b; // 正确:使用一个枚举成员
}
};
}
常规的访问保护规则对局部类同样适用
外层函数对局部类的私有成员没有任何访问特权。
当然,局部类可以将外层函数声明为友元;或者更常见的情况是局部类将其成员声明成公有的。
在程序中有权访问局部类的代码非常有限。局部类已经封装在函数作用域中,通过信息隐藏进一步封装就显得没什么必要了。
局部类中的名字查找
局部类内部的名字查找次序与其他类相似。
在声明类的成员时,必须先确保用到的名字位于作用域中,然后再使用该名字。
定义成员时用到的名字可以出现在类的任意位置。
如果某个名字不是局部类的成员,则继续在外层函数作用域中查找;如果还没有找到,则 在外层函数所在的作用域中查找。
嵌套的局部类
可以在局部类的内部再嵌套一个类。
此时,嵌套类的定义可以出现在局部类之外。
不嵌套类必须定义在与局部类相同的作用域中。
void foo()
{
class Bar {
public:
//...
class Nested;// 声明Nested类
};
//定义Nested类
class Bar::Nested
{
//...
};
};
和往常一样,当我们在类的外部定义成员时,必须指明该成员所属的作用域。因此在上面的例子中,Bar::Nested的意思是Nested是定义在Bar的作用域内的一个类。
局部类内的嵌套类也是一个局部类,必须遵循局部类的各种规定。嵌套类的所有成员都必须定义在嵌套类内部。