30

私はC++を初めて使用し、コードの分離についていくつかの一般的な質問がありました。私は現在、すべて1つのファイルに小さなアプリケーションを作成しました。私が今やりたいのは、これを別々のファイルに変換して、同様のコードなどが含まれるようにすることです。今の私の本当の質問は、どうすれば物事を分離する方法を知ることができるかということです。コードを分離する必要がある目に見えないマージンはどれくらいですか?

また、ヘッダーファイルのポイントは何ですか?コンパイル中にリンカーに含まれる前にコードで使用できるように、メソッドとクラスを前方宣言するのですか?

方法やベストプラクティスについての洞察は素晴らしいでしょう、ありがとう!

4

4 に答える 4

28

ヘッダーファイルには、クラスと関数の宣言が含まれている必要があります。

ソースファイルには、クラスと関数の定義が含まれています。

ヘッダーファイルごとに1つの宣言、ソースファイルごとに1つの定義を設定するのが標準的な方法です(つまり、読みやすくなります)。ただし、小さい(より単純なヘルパーを読む)オブジェクトの場合は、関連するより実質的なオブジェクトとグループ化することがあります。

例:クラスメニュー

Menu.h:     Contains the Menu declaration.
Menu.cpp:   Contains the Menu definition.

The reason header files contain the declarations is so that you can include them from multiple source files and thus each source file has exactly the same definition of each class and function.

Consider it this way:
If you did not have header files then you would need to have the class and/or function declarations (without) definitions in every source file, this means a copy of the same declaration in every file. Thus if you modify a class you need to make the same modification in every file. By the use of a header file you have the declaration in one place and thus only one object to modify.

于 2008-11-11T04:56:38.890 に答える
23

まず、必要なファイル以外のファイルで表示する必要のないヘッダーには何も入れないでください。次に、必要なものを以下に定義しましょう。

翻訳ユニット

変換ユニットは、コンパイルされている現在のコードであり、直接または間接的に、それに含まれるすべてのコードです。1つの変換ユニットが1つの.o/.objファイルに変換されます。

プログラム

これが、プロセスを形成するために実行できる1つのバイナリファイルにリンクされたすべての.o/.objファイルです。

異なる翻訳単位を持つことの主なポイントは何ですか?

  1. 依存関係を減らして、1つのクラスの1つのメソッドを変更した場合に、プログラムのすべてのコードを再コンパイルする必要はなく、影響を受ける変換ユニットのみを再コンパイルする必要があります。アン
  2. 他の翻訳ユニットがリンクしているときに表示されない翻訳ユニットのローカル名を使用することで、名前の衝突の可能性を減らします。

では、コードをさまざまな翻訳単位に分割するにはどうすればよいでしょうか。答えは「そうする」ということはありませんが、ケースバイケースで検討する必要があります。さまざまなクラスがあり、さまざまな翻訳単位に配置することができ、配置する必要があるため、多くの場合明らかです。

foo.hpp:

/* Only declaration of class foo we define below. Note that a declaration
 * is not a definition. But a definition is always also a declaration */
class foo;

/* definition of a class foo. the same class definition can appear 
   in multiple translation units provided that each definition is the same  
   basicially, but only once per translation unit. This too is called the  
   "One Definition Rule" (ODR). */
class foo {
    /* declaration of a member function doit */
    void doit();

    /* definition of an data-member age */
    int age;
};

いくつかの無料の関数とオブジェクトを宣言します。

/* if you have translation unit non-local (with so-called extern linkage)  
   names, you declare them here, so other translation units can include  
   your file "foo.hpp" and use them. */
void getTheAnswer();

/* to avoid that the following is a definition of a object, you put "extern"  
   in front of it. */
extern int answerCheat;

foo.cpp:

/* include the header of it */
#include "foo.hpp"

/* definition of the member function doit */
void foo::doit() {
    /* ... */
}

/* definition of a translation unit local name. preferred way in c++. */
namespace {
    void help() {
        /* ... */
    }
}

void getTheAnswer() {
    /* let's call our helper function */
    help();
    /* ... */
}

/* define answerCheat. non-const objects are translation unit nonlocal  
   by default */
int answerCheat = 42;

bar.hpp:

/* so, this is the same as above, just with other classes/files... */
class bar {
public:
    bar(); /* constructor */
}; 

bar.cpp:

/* we need the foo.hpp file, which declares getTheAnswer() */
#include "foo.hpp"
#include "bar.hpp"

bar::bar() {
    /* make use of getTheAnswer() */
    getTheAnswer();
}

匿名の名前空間(上記のように)内の名前は、翻訳単位ローカルであるように見えるため、衝突しないことに注意してください。実際にはそうではなく、衝突しないように一意の名前を持っているだけです。翻訳ユニットのローカル名が本当に必要な場合(たとえば、Cコードが関数を呼び出すことができるようにcとの互換性があるため)、次のように実行できます。

static void help() { 
    /* .... */
}

ODRは、1つのプログラムにオブジェクトまたは非インライン関数の複数の定義を含めることはできないとも述べています(クラスはオブジェクトではなく型であるため、それらには適用されません)。したがって、非インライン関数をヘッダーに入れたり、「intfoo;」のようなオブジェクトを入れたりしないように注意する必要があります。ヘッダーで。これにより、リンカーがこれらのヘッダーを含む変換ユニットをリンクしようとしたときに、リンカーエラーが発生します。

私はあなたを少し助けることができると思います。長い答えでしたが、確かにどこかにエラーがあります。翻訳単位は厳密に別の方法(プリプロセッサの出力)で定義されていることを私は知っています。しかし、それを上記に含めることは大きな価値をもたらさないと私は思います、そしてそれは問題を混乱させるでしょう。本当のバグを見つけたら、遠慮なく私を叩いてください:)

于 2008-11-11T05:27:04.663 に答える
4

コードを異なるクラス/関数に分割する方法を決定することは、プログラミングの主要なタスクの 1 つです。これを行う方法についてはさまざまなガイドラインがあり、C++ とオブジェクト指向設計に関するチュートリアルを読んで開始することをお勧めします。

いくつかの基本的なガイドラインは、

  • 一緒に使うものをまとめる
  • ドメイン オブジェクト (ファイル、コレクションなど) のクラスを作成します。

ヘッダー ファイルを使用すると、クラスまたは関数を宣言し、それをいくつかの異なるソース ファイルで使用できます。たとえば、ヘッダー ファイルでクラスを宣言すると、

// A.h
class A
{
public:
    int fn();
};

その後、このクラスを複数のソース ファイルで使用できます。

// A.cpp
#include "A.h"
int A::fn() {/* implementation of fn */}

//B.cpp
#include "A.h"
void OtherFunction() {
    A a;
    a.fn();
}

そのため、ヘッダー ファイルを使用すると、宣言を実装から分離できます。すべて (宣言と実装) をソース ファイル (A.cpp など) に入れる場合は、それを 2 番目のファイルに含めるようにしてください。

// B.cpp
#include  "A.cpp" //DON'T do this!

その後、B.cpp をコンパイルできますが、プログラムをリンクしようとすると、リンカはオブジェクトが複数定義されていると不平を言います。これは、A の実装のコピーが複数あるためです。

于 2008-11-11T05:01:00.973 に答える
0

提案: 1. すぐにアプリケーション用のデザインを用意してください。2. デザインに基づいて、相互に作用する必要なオブジェクトを作成します。3. 新しく作成した設計に合わせて、既存のコードをリファクタリングまたは完全に変更します。

ヘッダー ファイルは、その機能を使用する他のクラスへのインターフェイスを提供します。

于 2009-02-18T16:27:38.730 に答える