10

「ユーティリティ」の方向にある機能を使用するたびに、どのオプションが最適かを考えてしまいます。たとえば、メッセージ構造体 (独自または外部)、エンコード/デコード コード、または作業中のコンテキストでのいくつかの便利な変換関数の出力などです。

私が考えるオプションは次のとおりです。

1) ヘルパー クラス/構造体の静的関数。

struct helper
{
    static bool doSomething(...);
};

2) 非メンバー関数。

namespace helper
{
    bool doSomething(...);
}

3) 静的非メンバー関数。

namespace helper
{
    static bool doSomething(...);
}

場合によっては、「ユーティリティ」で状態を初期化または保持する必要がある場合があるため、オプション 1 を使用して「グローバル」状態を回避します。ただし、保持する必要がある状態がない場合は、オプション 2 または 3 を使用する必要がありますか? オプション 2 と 3 の実際的な違いは何ですか?

を考慮することが重要で、これにアプローチするための好ましい方法はありますか? ありがとう!

4

3 に答える 3

13

オプション 2 と 3 の違いは、2 番目のケースでは、関数が翻訳単位の内部になることです。関数が cpp でのみ定義されている場合は、これをオプションにする必要があります (これは名前のない名前空間とほぼ同等です。これは考慮すべき 4 番目のオプションであり、3 とほぼ同等です)。

関数が異なる翻訳単位で使用される場合は、オプション 2 を使用する必要があります。関数は 1 回コンパイルされます (関数としてマークinlineし、ヘッダーで定義を提供しない限り)。オプション 3 では、コンパイラはそれを作成します。各翻訳単位の内部コピー。

オプション1の時点で、私はそれを避けます。Java や C# では、あらゆる場所でクラスを使用する必要があり、操作がオブジェクト パラダイムに適切にマッピングされていない場合は、ユーティリティ クラスを使用することになります。一方、C++ では、これらの操作を独立した関数として提供でき、余分なレイヤーを追加する必要はありません。ユーティリティ クラスを選択した場合は、オブジェクトの作成を無効にすることを忘れないでください。

関数がクラス レベルか名前空間レベルかは、ルックアップに影響し、ユーザー コードに影響します。静的メンバー関数は、クラス スコープ内にいる場合を除き、常にクラス名で修飾する必要がありますが、名前空間関数をスコープに入れるにはさまざまな方法があります。わかりやすい例として、一連の数学ヘルパー関数と呼び出しコードを考えてみましょう。

double do_maths( double x ) {
   using namespace math;
   return my_sqrt( x ) * cube_root(x);
}
// alternatively with an utility class:
double do_maths( double x ) {
   return math::my_sqrt(x) * math::cube_root(x);
}

どちらが読みやすいかは別の話ですが、私は前者を好みます。関数内で名前空間を選択し、操作に集中してルックアップの問題を無視できます。

于 2011-08-05T08:19:10.630 に答える
10

クラスを名前空間として使用しないでください。名前空間内でフリー (つまり非メンバー) 関数を使用するのが最も簡単です

さらに、複数の翻訳単位で使用する必要がある場合、関数は静的であってはなりません。そうしないと、複製されます。

クラスを名前空間として使用するのはばかげています。なぜインスタンス化しないクラスを作成するのですか?

何らかの状態を保持する必要がある場合は、どこかにグローバル状態を保持します: 関数内の静的変数、ユーティリティ グローバル オブジェクト (おそらく「プライベート」名前空間内、または翻訳単位の 1 つの静的グローバル変数として)。インスタンス化しないクラスを作成しないでください。

悲しいかな、C#/Java のバックグラウンドを持つ人々はこのばかげたことをするのに慣れていますが、それは彼らの言語設計者が一方的に無料の関数は悪であると判断したためです。彼らが撃たれるかどうかは宗教の問題です。

注意の最後の言葉として: グローバル状態はめったに使用されるべきではありません。実際、コードがいつ大きくなったかを制御できない方法で、コードをグローバル状態の存在に結び付けます。この結合を明示的にしない理由を常に自問する必要があります。

于 2011-08-05T08:08:27.363 に答える
-1

名前空間の非メンバー関数よりもわずかに優れた分離を提供するため、静的関数で struct を使用します (Koenig ルックアップを回避するため)。

私が避けたいことの例を挙げると:

namespace Foo
{
      struct A
      {
      };

      void f(const A& a) {}
}

void f(const Foo:A& a) { std::cout << "AAA"; }

int main(void)
{
      Foo::A a;
      f(a); // calls Foo:f
      return 0;
}
于 2011-08-05T08:17:16.340 に答える