12

クラスと名前空間の両方?

この質問は、私自身がますます使用しているパターンについてです。関連する概念のクラスと名前空間の両方を持つことです。これは主にC++言語のアーティファクトによって動機付けられていると思いますが、完全ではありません。

トップレベルの質問は次のとおりだと思います。これは良い考えですか?関連する概念のクラスと名前空間の両方を持っていますか?

下位レベルの質問:

これを行うための最良の方法は何ですか?

名前空間内にネストされたクラス?:

namespace Foo_Namespace {
   class Foo_Class {
       ...
   };
}

または、個別、ピア、クラス、名前空間?:

class Foo_Class {
    ...
};
namespace Foo_Namespace {
   // non-class free functions, etc.
}

名前空間内にクラスをネストすることに傾倒していることを認めなければなりません。それは醜い名前につながりますが。しかし、それを行ったとしても、どの命名規則を使用する必要がありますか。

以下は長すぎるため、本当に醜い名前になりますFoo_Namespace :: Foo_Class

namespace Foo_Namespace {
   class Foo_Class {
       ...
   };
}

名前にサフィックスやインジケーターを使用する必要はありません。

namespace Foo {
   class Foo {
       ...
   };
}

しかし、Foo :: bar()を見ると、それが名前空間:: Fooの無料の関数バー、つまり:: Foo :: bar()であるか、名前空間のFooクラスのメンバー関数であるかがわかりません。 :: Foo :: Foo :: bar()。

そして::Foo:: Foo :: barのような名前はまだそうではありません、うーん、いいです。

現在やっています

名前にサフィックスやインジケーターを使用する必要はありません。

namespace Foo_ns {
   class Foo {
       ...
   };
}

主な理由は、通常、最初にクラスを作成し、後で名前空間が適切であることに気付いたためです。

何年も使用していない命名規則を復活させる必要があるのではないかと思います。クラスの場合は_c、名前空間の場合は_ns:

namespace Foo_ns {
   class Foo_c {
       ...
   };
}

詳細:

上で述べたことを繰り返すことはしませんが、もう少し追加します。

クラスに加えて名前空間を使用することを私が知っている最も実際的な理由は、名前空間で自由関数の前方宣言を行うことは許可されていますが、クラスの一部のメソッドの前方宣言を行うことは許可されていないことです。つまり、クラスでは、すべてまたは何も宣言する必要があります。一般に無料の関数、特に名前空間の無料の関数では、断片的に宣言できます。パーツは、さまざまなヘッダーファイルで宣言できます。大規模なクラスの大規模なヘッダーのみのライブラリを#includeするのではなく、1つまたは2つの関数に対して前方宣言を使用できます。等。

(たとえば、 http: //google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Forward_Declarationsを参照してください。ただし、Googleはヘッダーを含めるのではなく、前方宣言に反対します。)

もう1つの理由は、名前空間によってクラス自体を小さく保つことができるためです。

クラスの最大の利点は、クラスをテンプレートに渡すことができるのに対し、名前空間は渡すことができないことです。

クラスには、Foo_ns / Foo_ns :: Foo_c / Foo_ns :: Foo_Helper_cなどのヘルパークラスが必要になることが多いため、クラスをピアFoo_ns / Foo_cとして使用するのではなく、名前空間Foo_ns / Foo_ns::Foo_c内にネストすることをお勧めします。クラスと名前空間がピアである場合、Foo_ns / Foo_cがあり、Foo_ns::Foo_Helper_cがあるのは奇妙に思えます。

Andrei Alexandresciuに同意するので、名前空間が好きです:http: //laser.inf.ethz.ch/2012/slides/Alexandrescu/1-C++%20course%20parts%201%20and%202.pdf

 Class methods vs. free functions

 • Conventional wisdom: methods are cool, free functions are so 1960s
 • Yet:
   ◦ Free functions improve encapsulation over methods
   ◦ Free functions may be more general
   ◦ Free functions decouple better
   ◦ Free functions support conversions on their left-hand argument

  Surprising fact #2

    Making a function a method should be
    your last, not first, choice

  (c) 2012– Andrei Alexandrescu. 32 / 59

クラスのメソッドよりも無料の関数を作成することをお勧めします。

ただし、テンプレートなど、クラスを使用する必要がある場合もあります。

命名規則

過去にFoo_ns/Foo_ns::Foo_cを使用しました

私は今Foo_ns/Foo_ns::Fooを使用しています

(ちなみに、私はCamelCaseではなくClass_Names_With_Underscores_and_Initial_Capsを使用する傾向があります。)

理にかなっている場合は、名前空間の_nsサフィックスを削除する場合があります。たとえば、名前空間とクラスが同じ名前である必要はありません。

同じ名前にするのは嫌いです。名前空間foo内のクラスFooのコンストラクターを考えてみましょう。

:: Foo :: Foo :: Foo()vs :: Foo_ns :: Foo :: Foo()

後者はそれほど良くはありませんが、少し混乱が少なくなります。

私は通常、名前空間にネストせずに、独自にクラスを作成すると思います。実際、名前空間内にネストされたクラスの方が優れていることに気付く前に、おそらくいくつかの静的メソッドを追加します。その段階までにリファクタリングするのは苦痛かもしれません、そして私は時々転送関数を作成することになります、それはクラス静的メソッドから名前空間のfree関数に、またはその逆に転送します。これにより、ボットがステップ1から名前空間のクラスにジャンプしたことを後悔しています。

結論

私の現在のBKMはFoo_ns/Foo_ns :: Foo、つまり

namespace Foo_ns {
   class Foo { 
   ...
   };
}

提案や改善をいただければ幸いです。

それとも私はこれをするために壊れていますか?

4

3 に答える 3

12

関連する関数を持つ名前空間にクラスを含めることをお勧めします。これの大きな理由は、引数依存のルックアップ(ADL)です。クラス型引数を持つ非メンバー関数を呼び出すと、関数名はそのクラスを囲む名前空間で検索されます。したがって、持っている場合は、次のように言います。

namespace foo {
  class bar { };
  void baz(bar);
}

を呼び出したい場合はbaz、それが含まれている名前空間を明示的に指定する必要はありません。次のようにするだけです。

foo::bar x;
baz(x);

を修飾しなくbazても、引数の型を囲む名前空間内にあるため、コンパイラは関数を検出しました。このように、C ++は、クラスの囲んでいる名前空間の内容をそのクラスのインターフェースの一部と見なします。このように、クラスのインターフェイスの一部である関数を非メンバーの非フレンド関数として実装すると、ADLが関数を見つけられるようになり、カプセル化が増加します関数がクラスの内部にアクセスする必要がない場合は、その関数をそのクラスのメンバーにしないでください。代わりに、そのクラスと同じ名前空間に配置してください。

ただし、名前空間がクラスと同じ名前であるかどうかは疑わしいです。通常、名前空間内には複数のクラスがあり、存在していなくても名前は異なります。名前空間の名前は、その中の単一のクラスだけでなく、その内容を説明する必要があります。

于 2013-01-19T21:27:33.523 に答える
4

分離 Foo_ClassしてFoo_Namespaceいて間違いなく間違っています。これにより、引数依存ルックアップを使用して、クラスで使用することを目的とした名前空間内の関数を見つけることができなくなります。

したがって、クラスタイプの引数を取る関数が名前空間にある場合は、クラスを名前空間内にネストする必要があります。

クラスと名前空間に同じ名前を使用すると、少し混乱し、場合によってはあいまいになる可能性があります。

名前空間に最初の大文字で名前を付けることも珍しいことです。クラス名が大文字で始まる場合は、名前空間fooとクラスに移動して、Fooを指定できますfoo::Foo

名前空間に複数のクラスが含まれることはありませんか?それは私には珍しいように聞こえます。名前空間には、1つのタイプだけでなく、そのすべての内容にちなんで名前を付けます。たとえば、socketクラスの場合はnetworking名前空間に配置します。

_cクラスの接尾辞は完全にばかげていると思います。_ns名前空間の接尾辞も好きではありません。保存の猶予はそれFoo_ns::Fooよりも優れているだけですが、代わりFoo::Fooにそれを作成しfoo::Fooます。

(残りの質問は、説明する必要のない「名前空間が優れている理由」のようです。名前空間は理由で言語に追加されました。また、インターフェイスの一部に非メンバーを使用することをお勧めします。 「メンバーである必要はありません。Googleは正しいです。一般に、前方宣言ではなくヘッダーを使用します。これにより、別のヘッダーで名前空間を再度開いて、名前空間に新しい名前を追加できるという事実が変わりません。これが利点です。」再記述。)

于 2013-01-19T21:25:03.773 に答える
2

私は「フラクタル」な名前が繰り返されるのを嫌い、それを避けようとします。あなたの場合、名前空間「Foo」やネストされたクラス「Type」のようなものを試してみます。他の名前の方が適切かもしれませんが、それは実際のユースケースによって異なります。これが私が何度も繰り返していることに気付くユースケースです:

namespace status {
    enum type {
        invalid,
        okay,
        on_fire
    };
    // debug output helper
    char const* to_string(type t);
}

列挙型はと呼ばれstatus::type、のような値を取ることができますstatus::okay。かなり読みやすい私見。

于 2013-01-19T21:28:32.970 に答える