ネストされたクラスは通常のクラスと同じですが、次のようになります。
- (クラス定義内のすべての定義と同様に)追加のアクセス制限があります。
- それらは与えられた名前空間、例えばグローバル名前空間を汚染しません。クラスBがクラスAに深く関連しているように感じても、AとBのオブジェクトが必ずしも関連していない場合は、クラスBにアクセスするには、Aクラス(Aと呼ばれる) ::クラス)。
いくつかの例:
関連するクラスのスコープに配置するための、クラスを公にネスト
クラスのSomeSpecificCollection
オブジェクトを集約するクラスが必要だとしますElement
。その後、次のいずれかを行うことができます。
2つのクラスを宣言しますSomeSpecificCollection
とElement
-悪い、名前「要素」は、一般的に十分可能名前の衝突を起こさせるためにあるので、
名前空間someSpecificCollection
を導入し、クラスsomeSpecificCollection::Collection
を宣言しますsomeSpecificCollection::Element
。名前の衝突のリスクはありませんが、さらに冗長になる可能性はありますか?
と2つのグローバルクラスSomeSpecificCollection
を宣言しますSomeSpecificCollectionElement
-これには小さな欠点がありますが、おそらく大丈夫です。
ネストされたクラスとしてグローバルクラスSomeSpecificCollection
とクラスElement
を宣言します。次に:
- Elementはグローバル名前空間にないため、名前が衝突する危険はありません。
SomeSpecificCollection
あなたの実装では、だけElement
で、他の場所ではSomeSpecificCollection::Element
-と同じように+-と見なされますが、より明確です。
- 「コレクションの特定の要素」ではなく、「特定のコレクションの要素」であることが簡単にわかります
SomeSpecificCollection
クラスでもあることは明らかです。
私の意見では、最後のバリアントは間違いなく最も直感的であり、したがって最高のデザインです。
強調させてください-より詳細な名前で2つのグローバルクラスを作成することと大きな違いはありません。それはほんの少しの詳細ですが、imhoはコードをより明確にします。
クラススコープ内に別のスコープを導入する
これは、typedefまたはenumを導入する場合に特に役立ちます。ここにコード例を投稿します。
class Product {
public:
enum ProductType {
FANCY, AWESOME, USEFUL
};
enum ProductBoxType {
BOX, BAG, CRATE
};
Product(ProductType t, ProductBoxType b, String name);
// the rest of the class: fields, methods
};
次に、次のように呼び出します。
Product p(Product::FANCY, Product::BOX);
しかし、のコード補完の提案を見ると、Product::
すべての可能な列挙値(BOX、FANCY、CRATE)がリストされていることがよくあり、ここでミスを犯しやすいです(C ++ 0xの強く型付けされた列挙型はそれを解決しますが、気にしないでください) )。
しかし、ネストされたクラスを使用してこれらの列挙型に追加のスコープを導入すると、次のようになります。
class Product {
public:
struct ProductType {
enum Enum { FANCY, AWESOME, USEFUL };
};
struct ProductBoxType {
enum Enum { BOX, BAG, CRATE };
};
Product(ProductType::Enum t, ProductBoxType::Enum b, String name);
// the rest of the class: fields, methods
};
その後、呼び出しは次のようになります。
Product p(Product::ProductType::FANCY, Product::ProductBoxType::BOX);
次にProduct::ProductType::
IDEを入力すると、提案された目的のスコープから列挙型のみが取得されます。これにより、ミスをするリスクも軽減されます。
もちろん、これは小さなクラスには必要ないかもしれませんが、列挙型がたくさんあると、クライアントプログラマーにとって簡単になります。
同様に、必要に応じて、テンプレート内で多数のtypedefを「整理」することができます。それは時々便利なパターンです。
PIMPLイディオム
PIMPL(Pointer to IMPLementationの略)は、ヘッダーからクラスの実装の詳細を削除するのに役立つイディオムです。これにより、ヘッダーの「実装」部分が変更されるたびに、クラスのヘッダーに応じてクラスを再コンパイルする必要がなくなります。
通常、ネストされたクラスを使用して実装されます。
Xh:
class X {
public:
X();
virtual ~X();
void publicInterface();
void publicInterface2();
private:
struct Impl;
std::unique_ptr<Impl> impl;
}
X.cpp:
#include "X.h"
#include <windows.h>
struct X::Impl {
HWND hWnd; // this field is a part of the class, but no need to include windows.h in header
// all private fields, methods go here
void privateMethod(HWND wnd);
void privateMethod();
};
X::X() : impl(new Impl()) {
// ...
}
// and the rest of definitions go here
これは、完全なクラス定義が重いまたは単に醜いヘッダーファイル(WinAPIを使用)を持つ外部ライブラリからの型の定義を必要とする場合に特に役立ちます。PIMPLを使用する場合は、WinAPI固有の機能をだけで囲み、に.cpp
含めないでください.h
。