私は、クラスの一部として使用するために最初に記述した数学関数の束があるC++プロジェクトに取り組んでいます。しかし、私はより多くのコードを書いているので、これらの数学関数がどこにでも必要であることに気付きました。
それらを置くのに最適な場所はどこですか?私がこれを持っているとしましょう:
class A{
public:
int math_function1(int);
...
}
そして、私が別のクラスを書くとき、それを使用することはできません(または、少なくとも、方法がわかりません)math_function1
他のクラスで。さらに、この関数の一部はクラスAに実際には関連していないことに気づきました。それらは最初にあるように見えましたが、今ではそれらが単なる数学関数であることがわかります。
この状況での良い習慣は何ですか?現在、私はそれらを新しいクラスにコピーして貼り付けていますが、これは最悪の習慣だと確信しています。
C++はメソッド以外の関数を問題なく持つことができます。それらがクラスに属していない場合は、それらをクラスに配置せず、グローバルまたは他の名前空間スコープに配置してください
namespace special_math_functions //optional
{
int math_function1(int arg)
{
//definition
}
}
プロジェクトの編成方法と使用しているデザインパターンの種類によって異なりますが、これが完全にユーティリティコードであるとすると、次のオプションがあります。
最初のオプションが最善の策であり、次の3つは有用性が限られていることに注意してください。ただし、C#またはJavaプログラマーがC++の作業を行っているため、またはC#またはJavaクラスの必須です。
すでに述べたように、コードをコピーして貼り付けることは、コード再利用の最悪の形態です。どのクラスにも属さない、またはいくつかのシナリオで使用される可能性がある関数がある場合、それらを配置するのに最適な場所はヘルパーまたはユーティリティクラスです。インスタンスデータを使用しない場合は、静的にすることができるため、ユーティリティクラスのインスタンスを作成して使用する必要はありません。
ネイティブC++の静的メンバー関数の説明については here を、マネージC++の静的クラスについては here を参照してください。コードを貼り付けた場所ならどこでも、このユーティリティクラスを使用できます。
たとえば.NETでは、Min()
やMax()
などが System.Math
クラス 。
すべての関数が数学関連であり、他の人が巨大なMath
クラスを持っている場合は、それをさらに分解して、TrigonometryUtilities
、EucledianGeometryUtilities
などのクラスを作成することができます。オン。
別のオプションは、上記の機能を必要とするクラスの基本クラスに共有機能を配置することです。問題の関数がインスタンスデータを操作する必要がある場合、これはうまく機能しますが、1つのベースを「使い果たす」ため、多重継承を避けて1つのベースクラスのみに固執したい場合、このアプローチも柔軟性が低くなります。一部の共有機能にアクセスするためのクラスです。
「ヘルパー関数」という用語を明確にします。 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
_(それは大きい!)全体への露出を制限し、人々が最も頻繁に書き込む最も一般的なコード行に対してよりクリーンなコードを書くことを可能にします。