ネストされたC ++クラスと列挙を使用することの長所と短所
質問
ネストされたパブリックC ++クラスと列挙を使用することの長所と短所は何ですか?たとえば、 printer
というクラスがあり、このクラスが出力トレイにも情報を保存している場合、次のようになります。
class printer
{
public:
std::string name_;
enum TYPE
{
TYPE_LOCAL,
TYPE_NETWORK,
};
class output_tray
{
...
};
...
};
printer prn;
printer::TYPE type;
printer::output_tray tray;
別の方法:
class printer
{
public:
std::string name_;
...
};
enum PRINTER_TYPE
{
PRINTER_TYPE_LOCAL,
PRINTER_TYPE_NETWORK,
};
class output_tray
{
...
};
printer prn;
PRINTER_TYPE type;
output_tray tray;
プライベート列挙型/クラスをネストすることの利点はわかりますが、パブリック列挙型/クラスの場合、オフィスは分割されます-スタイル選択のようです。
では、どちらを好むのですか?
解決
ネストされたクラス
クラス内にネストされたクラスにはいくつかの副作用がありますが、私は通常、欠陥を考慮します(純粋なアンチパターンでない場合)。
次のコードを想像してみましょう:
class A
{
public :
class B { /* etc. */ } ;
// etc.
} ;
または偶数:
class A
{
public :
class B ;
// etc.
} ;
class A::B
{
public :
// etc.
} ;
だから:
- 特権アクセス: A :: BはAのすべてのメンバー(メソッド、変数、シンボルなど)に特権アクセスを持ち、カプセル化を弱めます
- Aのスコープはシンボルルックアップの候補です。 B内のコードは、シンボルルックアップの候補としてAからの all シンボルを見るため、コードが混乱する可能性があります
- forward-declaration: Aの完全な宣言を行わずにA :: Bをforward-declareする方法はありません
- 拡張性: Aの所有者でない限り、別のクラスA :: Cを追加することは不可能です
- コードの冗長性:クラスにクラスを配置すると、ヘッダーが大きくなります。これを複数の宣言に分割することもできますが、名前空間のようなエイリアス、インポート、または使用を使用する方法はありません。
結論として、例外(たとえば、ネストされたクラスがネストクラスの親密な部分である場合...およびそれでも...)を除き、欠陥が大きさによって大きくなるため、通常のコードではネストされたクラスには意味がありません知覚される利点。
さらに、C ++名前空間を使用せずにネームスペースをシミュレートしようとする不器用な試みとして悪臭を放ちます。
プロ側では、このコードを分離し、プライベートの場合は「外部」からは使用できないようにします。クラス...
ネストされた列挙
長所:すべて。
コン:なし。
実際には、enumアイテムはグローバルスコープを汚染します:
// collision
enum Value { empty = 7, undefined, defined } ;
enum Glass { empty = 42, half, full } ;
// empty is from Value or Glass?
各列挙型を異なる名前空間/クラスに配置するだけで、この衝突を回避できます:
namespace Value { enum type { empty = 7, undefined, defined } ; }
namespace Glass { enum type { empty = 42, half, full } ; }
// Value::type e = Value::empty ;
// Glass::type f = Glass::empty ;
C ++ 0xがクラス列挙型を定義したことに注意してください:
enum class Value { empty, undefined, defined } ;
enum class Glass { empty, half, full } ;
// Value e = Value::empty ;
// Glass f = Glass::empty ;
まさにこの種の問題に。
他のヒント
大規模なプロジェクトで大きな問題になる可能性がある1つの欠点は、ネストされたクラスまたは列挙型の前方宣言を行うことができないことです。
独立したクラスの実装を操作する以外に、従属クラスを使用するつもりがない場合、ネストされたクラスは問題ありません。
「内部」を使用する場合です。物事が少しマンキーになり始める可能性があり、抽出器/挿入器ルーチンの記述を開始する必要がある、それ自体がオブジェクトとしてのクラス。きれいな状況ではありません。
このように互いに関連しているもののようにグループ化するには、クラスの代わりに名前空間を使用する必要があるようです。ネストされたクラスを作成する際に見た欠点の1つは、セクションを検索する際に理解しにくい非常に大きなソースファイルになってしまうことです。
ネストされたパブリックC ++クラスを使用することの長所と短所はありません。事実だけがあります。これらの事実は、C ++標準によって義務付けられています。ネストされたパブリックC ++クラスに関する事実が賛否両論あるかどうかは、解決しようとしている特定の問題に依存します。あなたが与えた例では、ネストされたクラスが適切であるかどうかについて判断することはできません。
ネストされたクラスに関する1つの事実は、それらが属するクラスのすべてのメンバーへの特権アクセスがあることです。ネストされたクラスがそのようなアクセスを必要としない場合、これは欠点です。ただし、ネストされたクラスがそのようなアクセスを必要としない場合、ネストされたクラスとして宣言されるべきではありません。クラス A が特定の他のクラス B への特権アクセスを許可したい場合があります。この問題には3つの解決策があります
- B を A の友達にする
- B を A のネストされたクラスにする
- B が必要とするメソッドと属性、 A の公開メンバーを作成します。
この状況では、カプセル化に違反するのは#3です。これは、 A が彼の友人およびネストされたクラスを制御しますが、パブリックメソッドを呼び出すクラスまたはパブリック属性にアクセスするクラスは制御しないためです。
ネストされたクラスに関する別の事実は、 A :: C を A のネストされたクラスとして追加することは、の所有者でない限り不可能であることですA 。ただし、ネストされたクラスには特権アクセスがあるため、これは完全に合理的です。 A :: C を A のネストされたクラスとして追加できる場合、 A :: C は A 特権情報へのアクセス許可。 youldはカプセル化に違反しています。それは基本的に friend
宣言と同じです: friend
宣言は、あなたの友人が他の人から隠れているという特別な特権をあなたに与えません。友だちは、非友だちから隠している情報にアクセスできます。 C ++では、誰かを友人と呼ぶことは利他的な行為であり、利己的な行為ではありません。クラスがネストされたクラスになることを許可する場合も同様です。
ネストされたパブリッククラスに関するその他の事実:
- AのスコープはBのシンボルルックアップの候補です:これが望ましくない場合は、 B を A の代わりに友人にしますネストされたクラス。ただし、まさにこの種のシンボル検索が必要な場合があります。
- A :: B は前方宣言できません: A および A :: B は密結合です。 A を知らなくても A :: B を使用できるのは、この事実を隠すだけです。
これを要約すると、ツールがニーズに合わない場合は、ツールを非難しないでください。ツールを使用したことで自分を責める。他の人はさまざまな問題を抱えている可能性があり、それに対してツールは完璧です。
paercebalは、ネストされた列挙型について私が言うすべてのことを述べました。
WRTのネストされたクラスは、特定のタイプのリソースを操作するクラスがあり、そのリソースに固有の何かを表すデータクラスが必要な場合に使用します。あなたの場合、output_trayは良い例かもしれませんが、クラスに含まれるクラスの外部から呼び出されるメソッドがある場合、または主にデータクラス以外のものである場合、ネストされたクラスは通常使用しません。また、通常、含まれているクラスが含まれているクラスの外部で直接参照されない限り、データクラスをネストしません。
したがって、たとえば、printer_manipulatorクラスがある場合、プリンター操作エラー用の包含クラスがありますが、プリンター自体は非包含クラスになります。
これが役立つことを願っています。 :)
ネストされたクラスをいつでもトップレベルのクラスに昇格させることができますが、既存のコードを壊さずに反対のことができない場合があることに注意してください。したがって、最初にネストされたクラスにすることをお勧めします。問題になり始めたら、次のバージョンでトップレベルのクラスにします。
それを外部に置くことの大きな欠点は、それがグローバル名前空間の一部になることです。列挙型または関連するクラスが実際にそのクラスにのみ適用される場合、それは理にかなっています。そのため、プリンターの場合、プリンターを含むすべてのものは、列挙PRINTER_TYPEに完全にアクセスできることを認識しますが、実際にはそれについて知る必要はありません。内部クラスを使用したことがあるとは言えませんが、列挙型の場合、内部クラスを保持する方が論理的です。別のポスターが指摘したように、グローバル名前空間の目詰まりは本当に悪いことになる可能性があるため、名前空間を使用して同様のアイテムをグループ化することもお勧めします。私は以前に大規模なプロジェクトに取り組んでおり、グローバル名前空間に自動完全リストを表示するのに20分しかかかりません。私の意見では、ネストされた列挙型と名前空間のクラス/構造体はおそらく最もクリーンなアプローチです。
enumをクラスに埋め込むことを推奨する投稿に同意しますが、それを行わないほうが理にかなっている場合があります(ただし、少なくとも名前空間に入れてください)。複数のクラスが異なるクラス内で定義された列挙型を利用している場合、それらのクラスは、他の具象クラス(列挙型を所有する)に直接依存しています。そのクラスはその列挙と他の責任を担当するため、それは確かに設計上の欠陥を表します。
そのため、他のコードがその列挙型のみを使用してその具体的なクラスと直接インターフェースする場合、列挙型をクラスに埋め込みます。そうでない場合は、名前空間などの列挙を保持するためのより良い場所を見つけます。
enumをクラスまたは名前空間に配置すると、intellisenseは、enum名を思い出そうとしているときにガイダンスを提供できます。確かに小さなことですが、時には小さなことが重要です。
Visual Studio 2008は、ネストされたクラスにインテリセンスを提供できないようです。そのため、ネストされたクラスを使用していたほとんどの場合、PIMPLイディオムに切り替えました。クラスでのみ使用される場合は常にクラスに列挙型を配置します。複数のクラスが列挙型を使用する場合は、クラスと同じ名前空間のクラスの外部に列挙型を配置します。
ネストされたクラスのconを見ることができます。汎用プログラミングを使用した方がよいでしょう。
小さなクラスが大きなクラスの外側で定義されている場合、大きなクラスをクラステンプレートにして、任意の" little"を使用できます。大きなクラスで将来必要になるかもしれないクラス。
汎用プログラミングは強力なツールであり、私見では、拡張可能なプログラムを開発する際に留意する必要があります。奇妙なことに、誰もこの点に言及していない。
私がまだ遭遇したネストされたクラスの唯一の問題は、ネストされたクラス関数で、C ++が外側のクラスのオブジェクトを参照させないことでした。 " Enclosing :: this"とは言えません
(しかし、方法はあるのでしょうか?)