巢狀的 ClassesStructures

classstruct 本身也可以包含另一個 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 開始,巢狀類及其成員被視為封閉類的 friends,並且可以根據通常的訪問規則訪問其所有成員; 如果巢狀類的成員需要能夠評估封閉類的一個或多個非靜態成員,則必須將它們傳遞給例項:

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

在上面的例子中,BaseOuterDerivedOuter 分別提供成員型別 Inner,分別為 BaseInner_DerivedInner_。這允許在不破壞封閉類的介面的情況下匯出巢狀型別,並允許以多型方式使用巢狀型別。