web-dev-qa-db-ja.com

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

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

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

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

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

この状況での良い習慣は何ですか?現在、私はそれらを新しいクラスにコピーして貼り付けていますが、これは最悪の習慣だと確信しています。

51
coconut

C++はメソッド以外の関数を問題なく持つことができます。それらがクラスに属していない場合は、それらをクラスに配置せず、グローバルまたは他の名前空間スコープに配置してください

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

プロジェクトの編成方法と使用しているデザインパターンの種類によって異なりますが、これが完全にユーティリティコードであるとすると、次のオプションがあります。

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

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

7
rjzii

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

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

たとえば.NETでは、Min()Max()などが System.Mathクラス

すべての関数が数学関連であり、他の人が巨大なMathクラスを持っている場合は、それをさらに分解して、TrigonometryUtilitiesEucledianGeometryUtilitiesなどのクラスを作成することができます。オン。

別のオプションは、上記の機能を必要とするクラスの基本クラスに共有機能を配置することです。問題の関数がインスタンスデータを操作する必要がある場合、これはうまく機能しますが、1つのベースを「使い果たす」ため、多重継承を避けて1つのベースクラスのみに固執したい場合、このアプローチも柔軟性が低くなります。一部の共有機能にアクセスするためのクラスです。

1
PersonalNexus

「ヘルパー関数」という用語を明確にします。 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::cout_および_std::endl_!)の序文を書くのは面倒に思われますが、それは目的を果たします。

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

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

持ち込むことができます。

便利なテクニックは名前空間構成です。特定の_.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_(それは大きい!)全体への露出を制限し、人々が最も頻繁に書き込む最も一般的なコード行に対してよりクリーンなコードを書くことを可能にします。

0
Chris Reid