web-dev-qa-db-ja.com

C ++-ネストされたクラスのポイントは何ですか?

私はC++の少しを勉強していて、今はJavaと類似しているのと戦っています。私はJavaの内部クラスの目的を知っていますが、今C++でネストされたクラスを使用しようとしていますそして「コンテナ」クラスのプライベート属性がネストされたクラスによって表示されないことを発見しました、それでなぜそれらを使用する必要がありますか?また、それらの属性を表示する方法はありますか?

33
Leo91

私はC++について少し勉強しており、現在はJavaと類似していることと闘っています。

まず、C++のネストされたクラスはsimilar Javaでstaticネストされたクラスと呼ぶことに注意してください。 Java ネストされたクラスを再現するためのC++構文はありません。

「コンテナ」クラスのプライベート属性が内部クラスから見えないことを発見しました...

C++ 98

C++では、内部クラスはnormalクラスと異なりませんmembersクラスではないため、コンテナークラスのプライベートメンバーにアクセスできません(JavaまたはC#)。

C++ 03

ネストされたクラスはクラスメンバーですが、アクセスできるものには制限が適用されます(この回答の最後にあるセクション奇妙なものも参照)。これは標準の欠陥と見なされています( DR45 を参照)。一部のコンパイラは、C++ 03(特にGCC、Jonathan Wakelyがこれを見つけてくれたおかげで、GCC )。

C++ 11

このルールはC++ 11で変更され、ネストされたクラスがコンテナークラスのプライベートメンバーにアクセスできるようになりました。 §11.7から:

ネストされたクラスはメンバーであり、他のメンバーと同じアクセス権を持っています。

もちろん、静的でないメンバーにアクセスするにはインスタンスが必要です。


...それで、なぜそれらを使用する必要があるのですか?

それらは、関連するクラスをグループ化するための実装詳細であり、他の言語での使用法と同じissuesを持っています(初心者向けの明確さ、主要)。 IMOの最大の利点は、たとえば次のような場合、カプセル化です。

class stream {
    virtual void write(const std::string text) = 0;
};

class channel {
public:
    virtual stream* get_stream() = 0;

    // Other methods...
};

class tcp_channel : public channel {
public:
    virtual stream* get_stream() {
        return new tcp_stream(this);
    }

private:
    class tcp_stream : public stream { /* implementation */ };
};

substituteネストされた名前空間にいくつかの状況で役立ちます:

class protocol {
public:
    virtual void create_connection() = 0;

    class tcp : public protocol { /* implementation */ };
    class shared_memory : public protocol { /* implementation */ };
    class named_pipes: public protocol { /* implementation */ };
};

auto media = protocol::tcp();

または、実装の詳細を非表示にするには:

class file_system_entry {
public:
    class file : public file_system_entry { };
    class directory : public file_system_entry { };

    std::time_t get_last_modified() { ... }

    void remove() { ... }
    virtual void copy_to(std::string path) = 0;

private:
    class local_handle {
        // Implementation details
    } _handle;
};

他にも多くの使用パターンがあります(- C++でネストされたクラスを使用する理由は? を参照してください)。全員が正しく理解するわけではないことを覚えておいてください理解する(そして使用してください! )それら。参照 ネストされたC++クラスと列挙を使用することの長所と短所?

また、それらの属性を表示する方法はありますか?

C++ 11より前のバージョンでは(もちろん、friendsとして宣言しない限り次の段落を参照しない限り)、この機能が必要な場合は、C++ 11コンパイラ(この機能をサポートする)を使用するだけではできません。 GCCは(ずっと前から)実行し、MSVCも実行しますが、他のコンパイラについては知りません。

入れ子になった友達

C++ 11のアクセスルールとフレンドクラスに違いはありますか?一般に、それらはほとんど同等ですautomatic =アクセスはそれほど冗長ではありません):

class container {
public:
    class nested;
    friend class nested;

    class nested { };
};

に比べ:

class container {
public:
    class nested { };
};

ただし、前方宣言では、いくつかの副作用があります。また、アクセシビリティの観点からは、これらは同等であることを覚えておいてください(友情のようなアクセスは、継承も推移的もありません)。これらの例はコンパイルされません:

class external : public container::nested {
public:
    // No: only class declared inside "container"
    // has access to private members, we do not inherit that 
    void foo(container obj) { /* access a private member of obj*/ }
};

// No, "container" has not access to "nested" private members,
// visibility isn't reciprocal
void container::foo(container::nested obj) {
    // Access some private member of obj
}

// No, we don't have anything to do with container,
// visibility isn't transitive
void friendOfNested(container obj) {
    // Access some private member of obj
}

完全に同等ですか? いいえcontainerのフレンドのプライベートメンバーは、C++ 11のネストされたクラスの場合はnestedでアクセスできますが、nestedcontainerのフレンドである場合はアクセスできません。この概説された構造を考えると:

class container;

class another {
    friend class container;     
};

class container {
public:
    class nested { };   
};

nestedanotherのプライベートメンバーにアクセスできます。

void container::nested::foo(another obj) {
    obj.somePrivateMember = 0;
}

nestedcontainerメンバーであるため機能し、友情の推移的な制限は適用されません。 C++ 11より前は、nestedcontainerのフレンドとして宣言すると、フレンドシップが推移的でないため、そのコードはコンパイルされません。

変なこと

ネストされたクラスをそのコンテナのフレンドとしていつでも宣言できると思いますか?実際には標準[////] said (SO/IEC 14822:2003(E)、11.8):

クラスの友達は、クラスのメンバーではない関数またはクラスです...

次にすべきではないnestedcontainerのフレンドとして宣言できる:C++ 03ではネストされたクラスはクラスメンバーです(ただし、標準では明示的にコンテナプライベートへのアクセス権はなく、フレンドになることもできません)コンテナークラスの)。希望はなかったようです。幸い、ほとんどのコンパイラーは(標準が言ったことに関係なく)許可してくれました。

46
Adriano Repetti

別の優れたカプセル化手法を提供します。 1つのクラスを完全に別のクラスのnamespace内に配置すると、コードベースの他の部分に対する可視性が低下します。これにより、スケーラビリティが実現し、メンテナンスの負担が軽減されます。

関数オブジェクトは、このような方法でコーディングされることがよくあります。

5
Bathsheba

違いは同じではありません。

Javaのinnerクラスはouterクラスのオブジェクトに接続されていると想定されるオブジェクトを作成するため、内部クラスのメソッドから外部クラスのメンバーにアクセスできます明示的にポインタまたは参照を作成することなく。 C++はそれを行いません。 a nestedクラスは、その定義が別のクラスの定義内にネストされているクラスです。これはカプセル化には便利ですが、それだけです。その型のオブジェクトに、含まれている型のオブジェクトを魔法のように知らせることは意図されていません。

1
Pete Becker