クラスに関係のない関数はどこに置くべきですか?


47

私はクラスの一部として使用するために最初に書いた数学関数の束があるC ++プロジェクトに取り組んでいます。もっとコードを書いているうちに、どこでもこれらの数学関数が必要だと気づきました。

それらを置くのに最適な場所はどこですか?私がこれを持っているとしましょう:

class A{
    public:
        int math_function1(int);
        ...
}

そして、私が別のクラスを書くとき、私はその他のクラスでそれを使用することはできません(または、少なくともその方法を知りません)math_function1。さらに、この関数の一部はクラスAに実際には関連していないことに気付きました。それらは最初のように見えましたが、今では数学関数であることがわかります。

この状況での良い習慣は何ですか?今、私はそれらを新しいクラスにコピー&ペーストしていますが、これは最悪のプラクティスだと確信しています。


11
staticキーワードについて学習しましたか?
S.Lott

30
C ++では、ほとんどの場合、無料の関数がメンバー関数よりも優先されます。
パブ

4
すべてがクラス内になければならないと言うルールはありません。少なくともC ++では。
tdammers

2
静的メソッドの束を持つクラスよりも名前空間を好む
Nick Keighley

回答:


70

C ++には非メソッド関数があります。クラスに属さない場合は、クラスに入れず、グローバルまたは他の名前空間スコープに入れます。

namespace special_math_functions //optional
{
    int math_function1(int arg)
    {
         //definition 
    }
}

6
+1これは最も賢明なソリューションですが、追加のネームスペースは必要ないと思われます。
パブ

1
いいえ、必要ありません
jk。

27
いくつかの名前空間は、他のライブラリとの潜在的な名前の衝突を減らすのに役立ちます。
ビル・ドア

11
名前空間を使用すると、呼び出しがメソッドであるか関数であるかが明確になるため、便利です。(math_function1(42)現在のクラスのメンバーを呼び出す可能性があり、special_math_functions::math_function1(42)明らかに独立した関数を呼び出しています)。そうは言っ::math_function(42)ても、同じ曖昧さを排除します。
ipeet

2
名前空間は必要ありませんが、禁止されていません。したがって、なぜこの答えは言う// optional。味に応じて味付けします。
user253751

6

プロジェクトがどのように構成されているか、使用しているデザインパターンの種類によって異なります。これが厳密にユーティリティコードであると仮定すると、次のオプションがあります。

  • すべてにオブジェクトを使用する必要がない場合は、クラスラッパーを使用せずにオブジェクトをすべてファイルに入れるなどの簡単な操作を行うことができます。これは名前空間の有無にかかわらず可能ですが、将来の問題を防ぐために名前空間をお勧めします。
  • マネージC ++の場合、静的クラスを作成してそれらすべてを含めることができます。ただし、これは実際のクラスと同じようには機能せず、C ++のアンチパターンであるというのが私の理解です。
  • マネージC ++を使用していない場合は、静的関数を使用して、それらにアクセスし、それらすべてを単一のクラスに含めることができます。これは、適切なインスタンス化されたオブジェクトがアンチパターンである可能性のある他の関数もある場合に役立ちます。
  • 関数を含むオブジェクトのインスタンスが1つだけ存在するようにする場合は、ユーティリティクラスにシングルトンパターンを使用できます。これにより、非静的属性にアクセスできるようになり、将来の柔軟性も確保できます。これは使用が制限され、何らかの理由でオブジェクトが必要な場合にのみ実際に適用されます。オッズは、これを行うと、あなたはすでにその理由を知っているでしょう。

最初のオプションが最善策であり、次の3つは有用性が限られていることに注意してください。ただし、C ++またはJavaプログラマーがC ++の作業を行っている場合や、クラスの使用が必須のC#またはJavaコードで作業している場合に遭遇する可能性があります。


なぜダウン投票なのですか?
rjzii

10
私はダウンボーターではありませんが、おそらく静的関数またはシングルトンを持つクラスをアドバイスしているため、おそらくこの場合は無料の関数で問題ありません(C ++の多くの場合に受け入れられ、有用です)。
アントンゴロフ

@AntonGolov-リストで最初に言及したのは、無料の関数です。:)残りは、「すべてがクラスでなければならない!」を扱っている状況のための、よりOOP指向のアプローチです。環境。
rjzii

9
@Rob Z:ただし、C ++は「すべてはクラスでなければならない!」環境。
デビッドソーン

1
純粋な関数をクラスに強制するのはいつですか?OOP-cargo-cultに似ているようです。

1

既に述べたように、コードのコピーと貼り付けは、コードの再利用の最悪の形態です。クラスに属さない関数や、いくつかのシナリオで使用される可能性のある関数がある場合、それらを配置する最適な場所はヘルパークラスまたはユーティリティクラスです。インスタンスデータを使用しない場合は、静的にすることができるため、ユーティリティクラスのインスタンスを作成して使用する必要はありません。

ネイティブC ++の静的メンバー関数の説明についてはこちらを、マネージC ++の静的クラスについてはこちらをご覧ください。その後、コードを貼り付ける場所ならどこでもこのユーティリティクラスを使用できます。

たとえば.NETでは、Min()やのようなものMax()System.Mathクラスの静的メンバーとして提供されます。

すべての関数は数学的に関連しており、巨大な持ってotherwieseあなたが希望の場合Math、クラスを、あなたはさらにそれを打破し、同じようなクラスを持っている場合がありますTrigonometryUtilitiesEucledianGeometryUtilitiesそしてそうで。

別のオプションは、共有機能を、その機能を必要とするクラスの基本クラスに入れることです。問題の関数がインスタンスデータを操作する必要がある場合、これはうまく機能しますが、多重継承を避け、1つのベースクラスのみを使用する場合、このアプローチは柔軟性が低くなります。いくつかの共有機能にアクセスするためだけのクラス。


18
私見、静的メンバー以外の何もないユーティリティクラスは、C ++のアンチパターンです。名前空間の動作を完全に再現するためにクラスを使用していますが、これは実際には意味がありません。
ipeet

ユーティリティクラスに言及する場合は+1。C#のような言語では、すべてがクラス内にある必要があるため、さまざまな目的で多数のユーティリティクラスを作成することは非常に一般的です。これらのクラスをStaticとして実装すると、ユーティリティがさらに使いやすくなり、特に1つまたは2つの子孫だけが使用できるコードで基本クラスが肥大化する場合に、継承によって生じる頭痛の種が回避されます。同様の手法を他の言語に適用して、ユーティリティ関数に意味のあるコンテキストを提供することができ、それらをグローバルスコープに浮かせたままにすることはできません。
-S.ロビンズ

5
@ S.Robins:C ++のようなものは必要ありません。名前空間に置くだけで、まったく同じ効果が得られます。
DeadMG

0

「ヘルパー関数」という用語を明確にします。1つの定義は便利な機能であり、何らかの仕事をするためだけに常に使用します。これらはメイン名前空間に存在し、独自のヘッダーなどを持つことができます。他のヘルパー関数の定義は、単一のクラスまたはクラスファミリーのユーティリティ関数です。

// a general helper 
template <class T>
bool isPrinter(T& p){
   return (dynamic_cast<Printer>(p))? true: false;
}

    // specific helper for printers
namespace printer_utils {    
  namespace HP {
     print_alignment_page() { printAlignPage();}
  }

  namespace Xerox {
     print_alignment_page() { Alignment_Page_Print();}
  }

  namespace Canon {
     print_alignment_page() { AlignPage();}
  }

   namespace Kyocera {
     print_alignment_page() { Align(137,4);}
   }

   namespace Panasonic {
      print_alignment_page() { exec(0xFF03); }
   }
} //namespace

今、isPrinterそのヘッダを含む、任意のコードが利用可能ですが、print_alignment_page必要と using namespace printer_utils::Xerox;ディレクティブを。次のように参照することもできます

Canon::print_alignment_page();

より明確にするために。

C ++ STLには、std::そのクラスと関数のほぼすべてをカバーする名前空間がありますが、それらを17以上の異なるヘッダーに分類して、コーダーがクラス名、関数名などを書きたい場合に邪魔にならないようにします彼ら自身。

実際、using namespace std;ヘッダーファイルで使用することはお勧めしません。また、よく行われるように、の最初の行として使用することもお勧めしませんmain()std::5つの文字であり、多くの場合(特に使用に機能を一の欲求を前置きする雑用だstd::coutstd::endl!)しかし、それは目的を果たす行います。

新しいC ++ 11には、次のような特別なサービス用のサブ名前空間がいくつかあります。

std::placeholders,
std::string_literals,
std::chrono,
std::this_thread,
std::regex_constants

使用のために持ち込むことができます。

便利なテクニックは名前空間の構成です。1つは、特定の.cppファイルに必要なネームスペースを保持するカスタムネームスペースを定義し、using必要なネームスペース内の各項目のステートメントの代わりにそれを使用します。

#include <iostream>
#include <string>
#include <vector>

namespace Needed {
  using std::vector;
  using std::string;
  using std::cout;
  using std::endl;
}

int main(int argc, char* argv[])
{
  /*  using namespace std; */
      // would avoid all these individual using clauses,
      // but this way only these are included in the global
      // namespace.

 using namespace Needed;  // pulls in the composition

 vector<string> str_vec;

 string s("Now I have the namespace(s) I need,");

 string t("But not the ones I don't.");

 str_vec.push_back(s);
 str_vec.push_back(t);

 cout << s << "\n" << t << endl;
 // ...

この手法は、全体への露出を制限しstd:: namespace大きい!)、人々が最も頻繁に記述する最も一般的なコード行に対して、よりクリーンなコードを記述することを可能にします。


-2

さまざまな種類の整数や浮動小数点数で利用できるように、テンプレート関数に入れたい場合があります。

template <typename T>
T math_function1(T){
 ..
}

また、カスタムタイプに関連する演算子をオーバーロードしてテンプレートフレンドリーにすることで、巨大な数値や複素数などを表す適切なカスタムタイプを作成することもできます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.