C# のように、C++ で静的関数を分類する方法として使用するのが好きです。
Console::WriteLine("hello")
これは良いことですか、それとも悪いことですか?関数が頻繁に使用される場合は問題ないと思いますが、そうでない場合はメモリに圧力をかけますか?
どうstatic const
ですか?
しかし、それは良いか悪いか
最初に頭に浮かぶ形容詞は「不必要」です。C++ には自由な関数と名前空間があるのに、なぜそれらをクラス内で静的関数にする必要があるのでしょうか?
C# および Java のインスタンス化できないクラスで静的メソッドを使用することは回避策です。これらの言語には自由な関数 (つまり、クラスの一部としてではなく、名前空間に直接存在する関数) がないためです。C++ にはその欠陥はありません。名前空間を使用するだけです。
私は静的関数を使用することに賛成です。これらは、特にモジュールに編成されている場合 ( static class
C# の場合) に意味があります。
ただし、これらの関数がある種の外部 (コンパイル時 const ではない) データを必要とする瞬間、その関数をインスタンス メソッドにし、そのデータと共にクラスにカプセル化する必要があります。
一言で言えば、静的関数は問題ありませんが、静的データは悪いです。
静的関数を名前空間で置き換えることができると言う人は間違っています。簡単な例を次に示します。
class X
{
public:
static void f1 ()
{
...
f2 ();
}
private:
static void f2 () {}
};
ご覧のとおり、public static functionf1
は別の static ですが private function を呼び出しますf2
。
これは単なる関数のコレクションではなく、独自のカプセル化されたメソッドを持つスマート コレクションです。名前空間はこの機能を提供しません。
多くの人が「シングルトン」パターンを使用していますが、これは一般的な慣行であるという理由だけで、多くの場合、いくつかの静的メソッドと 1 つの静的データ メンバーを持つクラスが必要です。この場合、シングルトンはまったく必要ありません。また、メソッドの呼び出しはinstance()
、静的関数/メンバーに直接アクセスするよりも遅くなります。
名前空間を使用して、関数のコレクションを作成します。
namespace Console {
void WriteLine(...) // ...
}
メモリに関しては、関数は、静的メンバー関数として、または名前空間内で、関数の外部で同じ量を使用します。つまり、コード自体以外のメモリはありません。
静的データが悪い特定の理由の1つは、C++が異なる変換単位での静的オブジェクトの初期化順序について保証しないことです。実際には、これは、あるオブジェクトが別の翻訳単位で別のオブジェクトに依存している場合に問題を引き起こす可能性があります。Scott Meyersは、彼の著書More EffectiveC++のアイテム26でこれについて説明しています。
ここでフランクに同意します。静的(グローバル)関数には問題はありません(もちろん、それらが組織化されている場合).問題は、人々が「ああ、このビットのデータのスコープを少し広い」..滑りやすい斜面:)
実際に大局的に見ると..関数型プログラミング;)
私は静的関数で構成されるクラスを作成する傾向がありますが、これを行う「正しい方法」は通常、代わりに名前空間を使用することだと言う人もいます。(C++ に名前空間ができる前に、私は自分の習慣を身につけました。)
ところで、静的データと関数のみで構成されるクラスがある場合は、コンストラクターをプライベートとして宣言する必要があるため、誰もそれをインスタンス化しようとしません。(これが、クラスではなく名前空間を使用することを主張する理由の 1 つです。)
静的関数の問題は、カプセル化を破る設計につながる可能性があることです。たとえば、次のようなものを書いていることに気付いたとします。
public class TotalManager
{
public double getTotal(Hamburger burger)
{
return burger.getPrice() + burget.getTax();
}
}
...その場合、設計を再考する必要があるかもしれません。静的関数では、多くの場合、クラスの API を混乱させ、一般的に物事をより複雑にするセッターとゲッターを使用する必要があります。私の例では、Hamburger の getter を削除して、getTotal() クラスを Hamburger 自体に移動した方がよい場合があります。
編成には、すでに述べたように名前空間を使用します。
グローバルデータの場合、静的オブジェクトの初期化順序が不明であるという問題に役立つため、シングルトンパターンを使用するのが好きです。つまり、オブジェクトをシングルトンとして使用する場合、使用時に初期化されることが保証されます。
また、静的関数がステートレスであることを確認して、スレッドセーフになるようにしてください。
私は通常、フレンド システムと組み合わせてのみスタティックを使用します。
たとえば、多くの (インライン化された) 内部ヘルパー関数を使用して、プライベート データの操作を含むものを計算するクラスがあります。
もちろん、これにより、クラス インターフェイスが持つ関数の数が増えます。それを取り除くために、元のクラスの .cpp ファイルでヘルパー クラスを宣言し (したがって、外部からは見えません)、元のクラスのフレンドにしてから、古いヘルパー関数を静的 (インライン) メンバーに移動します。古いパラメーターに加えて、参照ごとに古いクラスを渡すヘルパー クラスの関数。
これにより、インターフェイスがスリムに保たれ、無料のフレンド機能を大量にリストする必要がなくなります。インライン化もうまく機能するので、静的に完全に反対しているわけではありません。(できる限り避けていますが、このように使用するのが好きです。)