48

ネストされたパブリック 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;

プライベート列挙型/クラスをネストする利点はわかりますが、パブリックのものになると、オフィスが分割されます-スタイルの選択のようです.

それで、あなたはどちらを好みますか、そしてその理由は何ですか?

4

13 に答える 13

47

ネストされたクラス

クラス内にネストされたクラスには、通常は欠陥と見なされるいくつかの副作用があります(純粋なアンチパターンではない場合)。

次のコードを想像してみましょう:

class A
{
   public :
      class B { /* etc. */ } ;

   // etc.
} ;

あるいは:

class A
{
   public :
      class B ;

   // etc.
} ;

class A::B
{
   public :

   // etc.
} ;

それで:

  • 特権アクセス: A :: Bは、カプセル化を弱めるAのすべてのメンバー(メソッド、変数、シンボルなど)への特権アクセスを持っています。
  • Aのスコープはシンボルルックアップの候補です: Bの内側からのコードは、Aからのすべてのシンボルをシンボルルックアップの可能な候補として見るため、コードを混乱させる可能性があります
  • 前方宣言: Aの完全な宣言を行わずにA::Bを前方宣言する方法はありません。
  • 拡張性: Aの所有者でない限り、別のクラスA::Cを追加することはできません。
  • コードの冗長性:クラスをクラスに入れると、ヘッダーが大きくなるだけです。これを複数の宣言に分割することはできますが、名前空間のようなエイリアス、インポート、または使用法を使用する方法はありません。

結論として、例外がない限り(たとえば、ネストされたクラスはネストされたクラスの親密な部分です...そしてそれでも...)、欠陥が認識された利点の大きさよりも重要であるため、通常のコードではネストされたクラスには意味がありません。

さらに、C++名前空間を使用せずに名前空間をシミュレートする不器用な試みのようににおいがします。

プロ側では、このコードを分離し、プライベートの場合は使用できないようにしますが、「外部」クラスからは...

ネストされた列挙型

長所:すべて。

短所:何もありません。

事実、列挙型アイテムはグローバルスコープを汚染します。

// 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 ;

まさにこの種の問題のために。

于 2008-10-19T18:15:21.590 に答える
6

大規模なプロジェクトで大きな問題になる可能性がある1つの欠点は、ネストされたクラスまたは列挙型に対して前方宣言を行うことが不可能であるということです。

于 2008-10-19T18:10:39.557 に答える
2

私の意見では、依存クラスを独立クラスの実装での作業以外に使用する予定がない場合は、ネストされたクラスで問題ありません。

「内部」クラスをそれ自体でオブジェクトとして使用したい場合は、物事が少し厄介になり始め、抽出/挿入ルーチンの作成を開始する必要があります。きれいな状況ではありません。

于 2008-10-19T18:15:32.683 に答える
2

このように相互に関連するもののようにグループ化するには、クラスの代わりに名前空間を使用する必要があるようです。ネストされたクラスを実行する際に私が見ることができた1つの欠点は、セクションを検索しているときに取得するのが難しい可能性がある非常に大きなソースファイルになってしまうことです。

于 2008-10-19T18:21:35.447 に答える
2

ネストされたパブリック C++ クラスを使用すること自体には、長所も短所もありません。あるのは事実だけです。これらの事実は、C++ 標準によって義務付けられています。ネストされたパブリック C++ クラスに関する事実が賛否両論であるかどうかは、解決しようとしている特定の問題によって異なります。あなたが示した例では、ネストされたクラスが適切かどうかについて判断することはできません。

ネストされたクラスに関する 1 つの事実は、それらが属するクラスのすべてのメンバーへの特権アクセスを持っていることです。ネストされたクラスがそのようなアクセスを必要としない場合、これは短所です。ただし、ネストされたクラスがそのようなアクセスを必要としない場合は、ネストされたクラスとして宣言されるべきではありません。クラスAが特定の他のクラスBに特権アクセスを許可したい場合があります。この問題には3つの解決策があります

  1. BをAの友達にする
  2. Bを A のネストされたクラスにする
  3. Bが必要とするメソッドと属性をA のパブリック メンバーにします。

この状況では、カプセル化に違反するのは #3 です。なぜなら、Aは友人や入れ子になったクラスを制御できますが、パブリック メソッドを呼び出したり、パブリック属性にアクセスしたりするクラスは制御できないためです。

ネストされたクラスに関するもう 1 つの事実は、 A の所有者でない限り、別のクラス A::C を A のネストされたクラスとして追加することはできないということです。ただし、ネストされたクラスには特権アクセスがあるため、これは完全に合理的です。A::CをAのネストされたクラスとして追加できる場合、A::CはAをだまして特権情報へのアクセスを許可させることができます。カプセル化に違反していること。基本的に宣言と同じです:friendfriend宣言は、あなたの友人が他の人から隠れているという特別な特権をあなたに与えるものではありません。これにより、あなたが友達以外から隠している情報に友達がアクセスできるようになります。C++ では、誰かを友人と呼ぶことは利己的な行為ではなく、利他的な行為です。クラスがネストされたクラスになることを許可する場合も同様です。

ネストされた public クラスに関するその他の事実:

  • A のスコープは B のシンボル ルックアップの候補です。これを望まない場合は、ネストされたクラスの代わりにBをAのフレンドにします。ただし、まさにこの種のシンボル検索が必要な場合があります。
  • A::Bは前方宣言できません。Aと:: Bは密結合です。Aを知らずにA::Bを使用できることは、この事実を隠すだけです。

これを要約すると、ツールがニーズに合わない場合でも、ツールを責めないでください。ツールを使用したことで自分を責めます。他の人は別の問題を抱えている可能性があり、ツールは完璧です。

于 2013-08-31T21:24:14.610 に答える
1

入れ子になったクラスは後でいつでも最上位クラスに昇格できますが、既存のコードを壊さずにその逆を行うことはできない場合があることに注意してください。したがって、最初にネストされたクラスにして、それが問題になり始めたら、次のバージョンでトップレベル クラスにすることをお勧めします。

于 2014-03-28T17:05:55.637 に答える
1

paercebal は、ネストされた列挙型について私が言うことすべてを言いました。

WRT のネストされたクラス、私の一般的でほぼ唯一の使用例は、特定の種類のリソースを操作するクラスがあり、そのリソースに固有のものを表すデータ クラスが必要な場合です。あなたの場合、 output_tray は良い例かもしれませんが、クラスに含まれるクラスの外部から呼び出されるメソッドがある場合、または主にデータクラス以上である場合、ネストされたクラスは通常使用しません。含まれているクラスが含まれているクラスの外部で直接参照されない限り、通常はデータ クラスもネストしません。

したがって、たとえば、printer_manipulator クラスがある場合、プリンター操作エラー用のクラスが含まれている可能性がありますが、プリンター自体は含まれていないクラスになります。

お役に立てれば。:)

于 2008-10-19T20:39:17.603 に答える
0

列挙型をクラスに埋め込むことを提唱する投稿には同意しますが、そうしない方が理にかなっている場合があります (ただし、少なくとも名前空間に入れてください)。複数のクラスが異なるクラス内で定義された列挙型を利用している場合、それらのクラスは (列挙型を所有する) 他の具象クラスに直接依存しています。そのクラスはその列挙型やその他の責任を負うため、これは確かに設計上の欠陥を表しています。

したがって、他のコードがその列挙型のみを使用してその具体的なクラスと直接やり取りする場合は、列挙型をクラスに埋め込みます。それ以外の場合は、名前空間など、列挙型を保持するためのより適切な場所を見つけてください。

于 2008-10-20T04:35:38.670 に答える
0

列挙型をクラスまたは名前空間に配置すると、列挙型の名前を覚えようとしているときに、Intellisense がガイダンスを提供できます。確かに小さなことですが、時には小さなことが重要です。

于 2008-10-20T04:54:36.793 に答える
0

私にとって、外部に置くことの大きな欠点は、それがグローバル名前空間の一部になることです。列挙型または関連するクラスが実際にそのクラスにのみ適用される場合、それは理にかなっています。したがって、プリンターの場合、プリンターを含むすべてのものは、列挙型 PRINTER_TYPE への完全なアクセス権を持っていることを知っていますが、実際にはそれについて知る必要はありません。内部クラスを使用したことがあるとは言えませんが、列挙型の場合、内部に保持する方が論理的です。別の投稿者が指摘しているように、名前空間を使用して同様のアイテムをグループ化することも良い考えです。グローバル名前空間を詰まらせることは本当に悪いことになる可能性があるからです。私は以前、大規模なプロジェクトに取り組んでおり、グローバル名前空間でオートコンプリート リストを表示するだけで 20 分かかりました。

于 2008-10-19T19:13:34.610 に答える
0

Visual Studio 2008 は入れ子になったクラスに IntelliSense を提供できないようです。そのため、以前は入れ子になったクラスを使用していたほとんどの場合、PIMPL イディオムに切り替えました。列挙型は、そのクラスでのみ使用される場合は常にクラス内に配置し、複数のクラスが列挙型を使用する場合はクラスと同じ名前空間内のクラス外に配置します。

于 2008-10-20T18:15:02.107 に答える
0

ネストされたクラスには、ジェネリック プログラミングを使用したほうがよいという短所があります。

小さなクラスが大きなクラスの外で定義されている場合、大きなクラスをクラス テンプレートにして、将来必要になる可能性のある「小さな」クラスを大きなクラスで使用できます。

ジェネリック プログラミングは強力なツールであり、私見ですが、拡張可能なプログラムを開発するときは、このことを念頭に置いておく必要があります。奇妙なことに、誰もこの点について言及していません。

于 2013-09-11T12:03:46.490 に答える
-1

私が遭遇した入れ子になったクラスの唯一の問題は、C++ では、入れ子になったクラス関数で、囲んでいるクラスのオブジェクトを参照できないことでした。「囲んで::これ」とは言えません

(でも、もしかしたら方法があるかも?)

于 2013-05-31T10:37:51.800 に答える