web-dev-qa-db-ja.com

cppファイルのクラスなしでのみ機能します!良いデザインかどうか?

私が取り組んでいるプロジェクトを進めていたときに、関数と静的変数のみが含まれているcppファイルに遭遇しました。内部関数を呼び出す必要がある場合は常に、cppファイルが含まれます。

これは良いデザインですか?これらのタイプのデザインの長所と短所は何ですか?

例として。

//independent.cpp
#include "someclass.h"
static int var1;
static someclass object("hello");
static bool var2;

void foo(const char* dir, const char* filename){
...
}

somefile.cpp
#include "independent.cpp"
class sample{
public:
  void caller(){
  foo("c:\", "file.txt");
  }
}
3
ks2

これらのタイプのデザインの長所と短所は何ですか?

長所:書きやすく、読みやすく、簡単に呼び出すことができます。

短所:名前の競合、グローバル変数のリスク、オーバーライドが困難です。

彼らはなぜすべてをクラスに入れていないのかと思っていました!

関数がコンテキストを必要としない場合(ユーティリティ関数など)、クラス内でそれらに意味のある場所を見つけるのは困難になります。クラスはコードとインスタンスを編成するための非常に便利な方法であるのと同様に、面倒な価値がないシナリオもあります。

また、クラスに入れるのはそれほど難しいことではないと主張することもできます。それは妥当なポイントですが、作者はそれに付加価値を見出していませんでした。

これは良いデザインですか?

私は100%同意します Emilio Garavagliaの考え: デザインが良いか悪いかは、状況に依存します。このようにするのは間違っているのではありませんが、一部のプロジェクトのスコープでは不可能かもしれません。

8
JvR

それらは、提供されているC++クラスとは異なるシングルトンオブジェクトパラダイムに従うだけです。

cPPファイルの静的変数は外からは見えませんが、クラスのプライベートメンバーはクラスで宣言する必要があります(ヘッダーで指定する必要があります)。

これらの変数を単に到達不能にしたい場合は、privateアクセスが適切ですが、それらを変数secretにしたい場合は、サイズが必要なクラスの「メンバー」にすることはできません。知られている。

そして、それらが一度だけ存在する必要がある場合、クラスはシングルトン(つまり、グローバル関数によってアクセスされる静的な隠し変数)である必要があり、非常に限定されたコンテキストで変数自体をグローバルにすることとそれほど変わりません。

これは、Win32またはGTK +にある、古き良きCベースのOOP=実装です。

デザインが良いか悪いかは、慣用句よりも文脈の問題です。

7

一連のユーティリティ関数をクラスとしてしないように実装する理由の1つは、状態のないクラスを作成するときに、すべてのインスタンスが同一であるため、関数のみを提供し、そのクラスをインスタンス化する意味はほとんどありません。データ型はコントラクトであるため、そのようなデータ型の定義には意味はありませんデータ/状態が何を表すかについて。状態またはデータがない場合、表すものはありません。

多くの場合、それはそれほど簡単ではありません。例として(これは実際の自己完結型クラスでおそらく必要なものです)、get_time_delta()関数があり、最後の呼び出し以降に費やされた時間を返したい状況を考えてください。 get_time_delta()関数-たとえば、ゲームでrender_scene()関数を実行するのにかかる時間を計算するときの一般的な状況。最初の呼び出しの時間と2番目の呼び出しの時間を保存し、2番目の呼び出しから最初の呼び出しを減算する必要があります-stateなし不可能です。 これを行う1つの方法は、クラスを実装してこれを保持することですstateプライベートメンバー変数として。ただし、これが汎用ユーティリティクラスの場合、get_time_delta()に関連するタイミングデータは、他のユーティリティ関数にも公開されるだけです。変数のスコープを最小化することが望ましいため、これは悪いことです。これに対処する1つの良い方法は、内部タイミングデータをstaticストレージクラス指定子で指定して、get_time_delta()関数に対してローカルにすることです。

現状では、前述の例ではnot必要stateクラス。では、なぜ最初にクラスが必要なのでしょうか。次のようなAPIがあるとします。

_unsigned int delta_time = Util::get_time_delta();
_

状態を保持していないため、Utilをインスタンス化する必要があるのは不合理です。したがって、get_time_delta()をクラスのstatic関数として宣言しますor名前空間内でget_time_delta()を宣言しますUtil

これは、「C++の方法」(意味が何であれ、読みすぎないこと)は、ユーティリティクラスのstaticメンバー関数としてではなく、ネームスペース内でユーティリティ関数を定義することです。

他の言語、つまりC#とJavaでは、utility patternと呼ばれるパターンがあり、がシミュレートします名前空間-ユーティリティ関数をクラスの静的メンバー関数として宣言することにより、「名前空間」の後ろに配置します。 C++では、組み込みの名前空間と独立した関数のため、これはまったく必要ありません。

3
zxcdw

私の推測では、これは"単一目的の実行可能ファイル"の束を作成するために使用されると思います。

(免責事項:私のC++の知識は他の回答者ほどよくありません-私の回答は推測です)


それを観察する

  • このアプローチでは、複数の再利用可能なクラス「someclass.h」などを含めることができます。したがって、「someclass.h」という名前のクラスのファミリが実際に適切なC++ OOPプラクティスに従っていると推測できます。

  • ただし、ビルドに複数の "independent.cpp"が含まれていると、名前の競合が発生する可能性があります。実際の名前の競合は、そのような2つのソースファイル(*)全体で同じ名前( "var1")を持つ2つの「静的」変数であるか、リンクエラーである可能性があります。

    • (*)externにならない限り。これらの行は、単一のcppファイルにインクルードされていることに注意してください。
  • したがって、実際のビルドでは、ビルドで生成された実行可能ファイルごとにリンクできる「independent.cpp」ファイルは1つだけであると推測できます。このようなファイルが複数ある場合、一度にリンクできるのはこれらの1つだけです。


(詳細)

「IndependentOne.cpp」と「IndependentTwo.cpp」の2つのファイルがあるとします。これらのファイル拡張子は、別のcppファイルに逐語的に含まれている場合は重要ではないことに注意してください。

「IndependentOne.cpp」と「IndependentTwo.cpp」のそれぞれに「var1」の定義が含まれているとします。

// IndependentOne.cpp
#include "someClassOne.h"
static someClassOne var1;

// IndependentTwo.cpp
#include "someClassTwo.h"
static someClassTwo var1; 

// SomeMainFile.cpp
#include "IndependentOne.cpp"
#include "IndependentTwo.cpp"  // ---- compiler error here
int main(int argc, char** argv) 
{ ... }

次に、var1が複数定義されます。これを解決するには、2つのファイルを手動でコピーして「IndependentOneAndTwo.cpp」に貼り付け、静的変数の名前を変更する必要があります。


それはいつ適切ですか?

  • 「OSコマンドラインからC++関数を呼び出す」必要があるときはいつでも、
  • 呼び出されるC++関数ごとに1つの実行可能ファイルが必要
  • 呼び出し間でデータは渡されません。またはデータは常にファイルシステムを介して渡されます

この哲学は一体何ですか?


実際に使用することを意図していない場合(ハードコードされたファイル名など)、それは何のために使用されますか?

  • C++関数の使い捨て「コマンドラインラッパー」。
    • これにより、C++関数を1つだけ呼び出すことができます。
    • パスがハードコーディングされているファイルまたはディレクトリを1つだけ読み書きします。

いくつかの代替設計は何ですか?

  • 目標が使い捨てコマンドラインラッパーの場合、これで問題ありません。
    • これらのファイルがexcluded fromである限り、本番ビルドです。
  • 目標が本番用コマンドラインアプリケーションの場合、
    • まともなコマンドライン解析ユーティリティを実装または使用するか、小さなスクリプトエンジンをC++クラスと統合して、「一度だけ呼び出して終了する」という制限を取り除きます。
  • 目標がソフトウェアテスト(ホワイトボックス/ユニットテスト)、の場合
    • 単体テストフレームワークを使用します。
0
rwong