嵌套的 ClassesStructures
class
或 struct
本身也可以包含另一个 class
/ struct
定义,称为嵌套类; 在这种情况下,包含类被称为封闭类。嵌套类定义被认为是封闭类的成员,但在其他方面是分开的。
struct Outer {
struct Inner { };
};
从封闭类的外部,使用范围运算符访问嵌套类。但是,从封闭类内部可以使用嵌套类而不使用限定符:
struct Outer {
struct Inner { };
Inner in;
};
// ...
Outer o;
Outer::Inner i = o.in;
与非嵌套的 class
/ struct
一样,成员函数和静态变量可以在嵌套类中定义,也可以在封闭名称空间中定义。但是,它们不能在封闭类中定义,因为它被认为是与嵌套类不同的类。
// Bad.
struct Outer {
struct Inner {
void do_something();
};
void Inner::do_something() {}
};
// Good.
struct Outer {
struct Inner {
void do_something();
};
};
void Outer::Inner::do_something() {}
与非嵌套类一样,嵌套类可以在以后声明和定义,前提是它们在直接使用之前定义。
class Outer {
class Inner1;
class Inner2;
class Inner1 {};
Inner1 in1;
Inner2* in2p;
public:
Outer();
~Outer();
};
class Outer::Inner2 {};
Outer::Outer() : in1(Inner1()), in2p(new Inner2) {}
Outer::~Outer() {
if (in2p) { delete in2p; }
}
Version < C++ 11
在 C++ 11 之前,嵌套类只能访问封闭类中的类型名称,static
成员和枚举器; 封闭类中定义的所有其他成员都是禁止的。
Version >= C++ 11
从 C++ 11 开始,嵌套类及其成员被视为封闭类的 friend
s,并且可以根据通常的访问规则访问其所有成员; 如果嵌套类的成员需要能够评估封闭类的一个或多个非静态成员,则必须将它们传递给实例:
class Outer {
struct Inner {
int get_sizeof_x() {
return sizeof(x); // Legal (C++11): x is unevaluated, so no instance is required.
}
int get_x() {
return x; // Illegal: Can't access non-static member without an instance.
}
int get_x(Outer& o) {
return o.x; // Legal (C++11): As a member of Outer, Inner can access private members.
}
};
int x;
};
相反,封闭类不被视为嵌套类的朋友,因此在未明确授予权限的情况下无法访问其私有成员。
class Outer {
class Inner {
// friend class Outer;
int x;
};
Inner in;
public:
int get_x() {
return in.x; // Error: int Outer::Inner::x is private.
// Uncomment "friend" line above to fix.
}
};
嵌套类的朋友不会自动被视为封闭类的朋友; 如果他们也需要成为封闭类的朋友,则必须单独声明。相反,由于封闭类不会自动被视为嵌套类的朋友,封闭类的朋友也不会被视为嵌套类的朋友。
class Outer {
friend void barge_out(Outer& out, Inner& in);
class Inner {
friend void barge_in(Outer& out, Inner& in);
int i;
};
int o;
};
void barge_in(Outer& out, Outer::Inner& in) {
int i = in.i; // Good.
int o = out.o; // Error: int Outer::o is private.
}
void barge_out(Outer& out, Outer::Inner& in) {
int i = in.i; // Error: int Outer::Inner::i is private.
int o = out.o; // Good.
}
与所有其他类成员一样,如果嵌套类具有公共访问权限,则只能从类外部命名。但是,只要你没有明确命名它们,就可以访问它们而不管访问修饰符。
class Outer {
struct Inner {
void func() { std::cout << "I have no private taboo.\n"; }
};
public:
static Inner make_Inner() { return Inner(); }
};
// ...
Outer::Inner oi; // Error: Outer::Inner is private.
auto oi = Outer::make_Inner(); // Good.
oi.func(); // Good.
Outer::make_Inner().func(); // Good.
你还可以为嵌套类创建类型别名。如果封闭类中包含类型别名,则嵌套类型和类型别名可以具有不同的访问修饰符。如果类型别名在封闭类之外,则它要求嵌套类或其中的 typedef
是公共的。
class Outer {
class Inner_ {};
public:
typedef Inner_ Inner;
};
typedef Outer::Inner ImOut; // Good.
typedef Outer::Inner_ ImBad; // Error.
// ...
Outer::Inner oi; // Good.
Outer::Inner_ oi; // Error.
ImOut oi; // Good.
与其他类一样,嵌套类既可以从其他类派生,也可以从其他类派生。
struct Base {};
struct Outer {
struct Inner : Base {};
};
struct Derived : Outer::Inner {};
通过允许程序员根据需要更新嵌套类,这在封闭类派生自另一个类的情况下非常有用。这可以与 typedef 结合使用,为每个封闭类的嵌套类提供一致的名称:
class BaseOuter {
struct BaseInner_ {
virtual void do_something() {}
virtual void do_something_else();
} b_in;
public:
typedef BaseInner_ Inner;
virtual ~BaseOuter() = default;
virtual Inner& getInner() { return b_in; }
};
void BaseOuter::BaseInner_::do_something_else() {}
// ---
class DerivedOuter : public BaseOuter {
// Note the use of the qualified typedef; BaseOuter::BaseInner_ is private.
struct DerivedInner_ : BaseOuter::Inner {
void do_something() override {}
void do_something_else() override;
} d_in;
public:
typedef DerivedInner_ Inner;
BaseOuter::Inner& getInner() override { return d_in; }
};
void DerivedOuter::DerivedInner_::do_something_else() {}
// ...
// Calls BaseOuter::BaseInner_::do_something();
BaseOuter* b = new BaseOuter;
BaseOuter::Inner& bin = b->getInner();
bin.do_something();
b->getInner().do_something();
// Calls DerivedOuter::DerivedInner_::do_something();
BaseOuter* d = new DerivedOuter;
BaseOuter::Inner& din = d->getInner();
din.do_something();
d->getInner().do_something();
在上面的例子中,BaseOuter
和 DerivedOuter
分别提供成员类型 Inner
,分别为 BaseInner_
和 DerivedInner_
。这允许在不破坏封闭类的接口的情况下导出嵌套类型,并允许以多态方式使用嵌套类型。