11

私はコードを整理するのが大好きなので、理想的にはファイルごとに1つのクラスが必要です。メンバー以外の関数がある場合は、ファイルごとに1つの関数が必要です。

理由は次のとおりです。

  1. コードを読むと、特定の関数またはクラスを見つける必要があるファイルが常にわかります。

  2. ヘッダーファイルごとに1つのクラスまたは1つの非メンバー関数である場合 include、ヘッダーファイルのときに混乱全体をインクルードすることはありません。

  3. 関数に小さな変更を加えると、その関数だけを再コンパイルする必要があります。

ただし、すべてを多くのヘッダーと多くの実装ファイルに分割すると、コンパイルが大幅に遅くなる可能性があります。私のプロジェクトでは、ほとんどの関数が特定の数のテンプレート化された他のライブラリ関数にアクセスします。そのため、コードは実装ファイルごとに1回ずつ、何度もコンパイルされます。プロジェクト全体のコンパイルには、現在1台のマシンで45分ほどかかります。約50個のオブジェクトファイルがあり、それぞれが同じコンパイルコストの高いヘッダーを使用します。

たぶん、ヘッダーファイルごとに1つのクラス(または非メンバー関数)を持つことは許容されますが、次の例のように、これらの関数の多くまたはすべての実装を1つの実装ファイルに入れますか?

// foo.h
void foo(int n);

// bar.h
void bar(double d);

// foobar.cpp
#include <vector>
void foo(int n) { std::vector<int> v; ... }
void bar(double d) { std::vector<int> w; ... }

繰り返しになりますが、foo関数のみまたはbar関数のみを含めることができ、1つのファイルであるため、foobar.cppプロジェクト全体のコンパイルが高速になるという利点があります。テンプレート化された構造をコンパイルする)をコンパイルする必要があるのは1回だけですが、とを別々にコンパイルした場合は2回コンパイルする必要があります。もちろん、上記の私の理由(3)は、このシナリオには当てはまりません。foo(){...}を変更した後、大きな可能性のあるファイル全体を再コンパイルする必要があります。std::vector<int>foo.cppbar.cppfoobar.cpp

私はあなたの意見が何であるか興味があります!

4

9 に答える 9

10

IMHO、アイテムを論理グループに結合し、それに基づいてファイルを作成する必要があります。

私が関数を書いているとき、互いに密接に関連している関数が半ダースほどあることがよくあります。私はそれらを 1 つのヘッダーと実装ファイルにまとめる傾向があります。

クラスを作成するときは、通常、ヘッダーと実装ファイルごとに 1 つの重いクラスに制限します。いくつかの便利な関数や小さなヘルパー クラスを追加するかもしれません。

実装ファイルが何千行もあることが分かった場合、それは通常、ファイルが多すぎて、分割する必要があることを示しています。

于 2009-02-10T05:25:06.570 に答える
8

私の意見では、ファイルごとに 1 つの関数が乱雑になる可能性があります。POSIX と ANSI C ヘッダーが同じ方法で作成されたと想像してください。

#include <strlen.h>
#include <strcpy.h>
#include <strncpy.h>
#include <strchr.h>
#include <strstr.h>
#include <malloc.h>
#include <calloc.h>
#include <free.h>
#include <printf.h>
#include <fprintf.h>
#include <vpritnf.h>
#include <snprintf.h>

ただし、ファイルごとに 1 つのクラスを使用することをお勧めします。

于 2009-02-10T05:34:16.300 に答える
4

ファイルごとに 1 つの外部関数の原則を使用します。ただし、このファイル内には、名前のない名前空間に、その関数を実装するために使用される他の「ヘルパー」関数がいくつかある場合があります。

私たちの経験では、他のいくつかのコメントとは対照的に、これには 2 つの主な利点がありました。1 つ目は、特定の API が変更されたときにのみモジュールを再ビルドする必要があるため、ビルド時間が短縮されることです。2 つ目の利点は、共通の命名スキームを使用することで、呼び出したい関数を含むヘッダーを探すのに時間を費やす必要がないことです。

// getShapeColor.h
Color getShapeColor(Shape);

// getTextColor.h
Color getTextColor(Text);

標準ライブラリが、ファイルごとに 1 つの (外部) 関数を使用しない良い例であることに同意しません。標準ライブラリは決して変更されず、適切に定義されたインターフェイスを備えているため、上記のいずれの点もそれらには当てはまりません。

そうは言っても、標準ライブラリの場合でも、個々の機能を分割することにはいくつかの潜在的な利点があります。1 つ目は、g++ が <iostream.h> と <iostream> のインクルードを警告していたのと同様の方法で、たとえばstrcpystrncpyのように、安全でないバージョンの関数が使用されている場合に、コンパイラが役立つ警告を生成できることです。

もう 1 つの利点は、 memmoveを使用したいときにメモリを含めることに悩まされることがなくなることです。

于 2009-02-10T10:50:05.660 に答える
1

あなたのアプローチにはいくつかの利点がありますが、いくつかの欠点があります。

  1. パッケージを含めることは悪夢です。必要な機能を取得するために、10 ~ 20 個のインクルードが必要になる場合があります。たとえば、STDIO または StdLib がこのように実装されているとします。

  2. 一般に、ファイルを切り替えるよりもファイルをスクロールする方が簡単なので、コードをブラウズするのは少し面倒です。ファイルが大きすぎるのは明らかに難しいですが、最新の IDE を使用しても、ファイルを必要なものに折りたたむのは非常に簡単で、多くの IDE には関数のショートカット リストがあります。

  3. Make ファイルのメンテナンスは面倒です。

  4. 私は小さな関数とリファクタリングの大ファンです。オーバーヘッド (新しいファイルの作成、ソース管理への追加など) を追加すると、1 つの関数を 3 つの部分に分割する代わりに、1 つの大きな関数を作成するだけで、より長い関数を作成するようになります。

于 2009-02-10T08:17:55.797 に答える
1

関数の一部を1 つ以上のクラスの静的メソッドとして再宣言できます。これにより、それらのいくつかを 1 つのソース ファイルにグループ化する機会 (および良い言い訳) が得られます。

ソース ファイルがオブジェクト ファイルと 1 対 1 であり、リンカーがオブジェクト ファイル全体をリンクしている場合、1 つのソース ファイルに複数の関数がある場合とない場合の 1 つの正当な理由: 実行可能ファイルが 1 つの関数を必要とし、別の関数を必要としない場合、それらを別々のソースファイルに入れます(リンカーが一方を他方なしでリンクできるようにするため)。

于 2009-02-10T05:34:37.813 に答える
1

また、ファイルごとに関数内でファイルを分割しようとしましたが、いくつかの欠点がありました。コードのリファクタリングに熱心でない限り (私はそうではありません)、関数が必要以上に大きくなる傾向があります (毎回新しい .c ファイルを追加する必要はありません)。

現在、1 つから 3 つの関数を .c ファイルに入れ、すべての .c ファイルをディレクトリ内の機能用にグループ化しています。Funcionality.h私が持っているヘッダー ファイルについてはSubfunctionality.h、必要に応じてすべての関数を一度に含めるか、パッケージ全体が必要ない場合は小さなユーティリティ関数だけを含めることができます。

于 2009-02-10T05:36:08.527 に答える
1

ヘッダー部分では、項目を論理グループに結合し、それに基づいてヘッダーファイルを作成する必要があります。これは非常に論理的な私見のようです。

ソース部分については、各関数の実装を個別のソースファイルに配置する必要があります (この場合、静的関数は例外です)。これは最初は論理的ではないように思えるかもしれませんが、コンパイラーは関数を認識していますが、リンカーは .o および .obj ファイルとそのエクスポートされたシンボルしか認識していないことを覚えておいてください。これにより、出力ファイルのサイズが大幅に変わる可能性があり、これは組み込みシステムにとって非常に重要な問題です。

glibc または Visual C++ CRT ソース ツリーをチェックアウト...

于 2009-02-10T07:14:38.803 に答える
1

私の古いプログラミングの教授は、保守性のために数百行のコードごとにモジュールを分割することを提案しました。私はもう C++ で開発を行っていませんが、C# ではファイルごとに 1 つのクラスに制限しており、オブジェクトに関係のないものがない限り、ファイルのサイズは問題になりません。#pragma regions を使用してエディターのスペースを適切に削減できます。C++ コンパイラーにそれらがあるかどうかはわかりませんが、ある場合は間違いなくそれらを使用します。

私がまだ C++ でプログラミングしていた場合は、ファイルごとに複数の関数を使用して関数を用途別にグループ化します。したがって、その「サービス」を定義するいくつかの関数を含む「Service.cpp」というファイルがあるかもしれません。ファイルごとに 1 つの関数を使用すると、何らかの形でプロジェクトに戻ってくるという後悔が生じます。

ただし、ファイルごとに数千行のコードを含める必要がない場合もあります。関数自体は、せいぜい数百行を超えるコードであってはなりません。関数は 1 つのことだけを行い、最小限に抑える必要があることを常に覚えておいてください。関数が複数のことを行う場合は、ヘルパー メソッドにリファクタリングする必要があります。

1 つのエンティティを定義するソース ファイルが複数あっても問題ありません。つまり、「ServiceConnection.cpp」「ServiceSettings.cpp」などです。

単一のオブジェクトを作成し、それが他のオブジェクトを所有している場合、複数のクラスを単一のファイルに結合することがあります。たとえば、「ButtonLink」オブジェクトを含むボタン コントロールの場合、それを Button クラスに結合できます。そうしないこともありますが、それは「その時の好み」の決定です。

あなたに最適なことをしてください。小規模なプロジェクトでさまざまなスタイルを少し試してみると効果的です。これが少し役立つことを願っています。

于 2009-02-10T05:48:24.270 に答える