.hファイル内または.cppファイル内のクラスの実装の違い
質問
ヘッダーにクラスをプロタイプし、効果的な.cppファイルに実装する通常のアプローチと比較して、ヘッダーファイルのみでクラスを宣言して実装することの違いはどれなのか疑問に思いました。
私が話していることをよりよく説明するには、通常のアプローチとの違いを意味します:
// File class.h
class MyClass
{
private:
//attributes
public:
void method1(...);
void method2(...);
...
};
//file class.cpp
#include "class.h"
void MyClass::method1(...)
{
//implementation
}
void MyClass::method2(...)
{
//implementation
}
およびジャストヘッダーアプローチ:
// File class.h
class MyClass
{
private:
//attributes
public:
void method1(...)
{
//implementation
}
void method2(...)
{
//implementation
}
...
};
主な違いを得ることができます。2番目の場合、コードは同じ実装のインスタンスをより多く生成する必要がある他のすべてのファイルに含まれているため、暗黙的な冗長性があります。一方、最初の場合、コードは単独でコンパイルされ、 MyClass
のオブジェクトを参照するすべての呼び出しは class.cpp
の実装にリンクされます。
しかし、他の違いはありますか?状況に応じて別のアプローチではなくアプローチを使用する方が便利ですか?また、ヘッダーファイルにメソッドの本体を直接定義することは、そのメソッドをインライン化するコンパイラーへの暗黙の要求であるということをどこかで読んだことがありますか?
解決
実際の主な違いは、メンバー関数の定義がヘッダーの本文にある場合、そのヘッダーを含む各翻訳単位に対して1回コンパイルされることです。プロジェクトに数百または数千のソースファイルが含まれ、問題のクラスがかなり広く使用されている場合、これは多くの繰り返しを意味する場合があります。各クラスが2つまたは3つの他のクラスによってのみ使用されている場合でも、ヘッダー内のコードが多いほど、作業が多くなります。
メンバー関数定義が独自の翻訳単位(.cppファイル)にある場合、それらは1回コンパイルされ、関数宣言のみが複数回コンパイルされます。
クラス定義で(宣言されているだけでなく)定義されているメンバー関数が暗黙的に inline
であることは事実です。しかし、 inline
は、人々が合理的に推測することを意味するものではありません。 inline
は、関数の複数の定義が異なる翻訳単位に現れ、後でリンクされることは合法であると言います。これは、異なるソースファイルが使用するヘッダーファイルにクラスが含まれている場合に必要であるため、言語は役立つように試みます。
inline
は、関数を便利にインライン化できるというコンパイラーへのヒントでもありますが、名前にかかわらず、オプションです。コンパイラが高度であればあるほど、インライン化に関する独自の決定を下すことができ、ヒントの必要性が少なくなります。実際のインラインタグよりも重要なのは、コンパイラが関数を使用できるかどうかです。関数が別の変換単位で定義されている場合、その呼び出しがコンパイルされたときに使用できないため、呼び出しがインライン化される場合は、コンパイラーではなくリンカーでなければなりません。
3つ目の方法を検討することで、違いをよりよく確認できる可能性があります。
// File class.h
class MyClass
{
private:
//attributes
public:
void method1(...);
void method2(...);
...
};
inline void MyClass::method1(...)
{
//implementation
}
inline void MyClass::method2(...)
{
//implementation
}
暗黙のインラインは邪魔にならないので、この「すべてのヘッダー」との間にいくつかの違いが残っています。アプローチ、および「ヘッダーとソース」アプローチ。コードを翻訳単位に分割する方法は、ビルド時に発生する結果に影響します。
他のヒント
はい、コンパイラはヘッダーファイルで直接宣言されたメソッドを次のようにインライン化しようとします。
class A
{
public:
void method()
{
}
};
ヘッダーファイルで実装を分離するには、次のような便利さを考えることができます。
- 同じコードが複数の翻訳単位に含まれるため、コードが肥大化することはありません
- コンパイル時間が短縮されます 劇的に。覚えておいてください ヘッダーファイルの変更 コンパイラは他のすべてを構築する必要があります 直接的または間接的に それを含めます。非常に 誰もが構築するためにイライラする 単にバイナリを追加するためだけに ヘッダーファイルのスペース。
実装を含むヘッダーを変更すると、そのヘッダーを含む他のすべてのクラスが強制的に再コンパイルおよび再リンクされます。
ヘッダーは実装よりも頻繁に変更されないため、実装を別のファイルに置くことで、コンパイル時間を大幅に節約できます。
他のいくつかの回答がすでに指摘しているように、はい、ファイルの class
ブロック内でメソッドを定義すると、コンパイラーがインラインになります。
はい、クラス定義内でメソッドを定義することは、 inline
を宣言することと同等です。他に違いはありません。ヘッダーファイルですべてを定義してもメリットはありません。
テンプレートメンバー定義もヘッダーファイルに含める必要があるため、ほとんどのテンプレートクラスのC ++でそのようなものが見られます(ほとんどのコンパイラは export
をサポートしていないため)。ただし、通常の非テンプレートクラスでは、メソッドを inline
として宣言する必要がない限り、これを行う意味はありません。
私にとっての主な違いは、ヘッダーファイルが「インターフェース」のようなものであることです。クラスについては、そのクラスのクライアントに、そのパブリックメソッド(サポートする操作)を伝えます。クライアントはそれらの特定の実装について心配する必要はありません。ある意味では、実装の変更からクライアントをカプセル化する方法です。cppファイルのみが変更されるため、コンパイル時間が大幅に短縮されるためです。
過去には、さまざまなCORBAディストリビューションの違いからシールドするモジュールを作成し、さまざまなOS /コンパイラ/ CORBA libの組み合わせで均一に動作することが期待されていました。ヘッダーファイルに実装すると、単純なインクルードを使用してプロジェクトに簡単に追加できます。同じ手法により、コードを呼び出すコードが別のライブラリまたは別のOSでコンパイルされているときに再コンパイルが必要になったときに、コードが同時に再コンパイルされることが保証されました。
だから私のポイントは、さまざまなプロジェクトで再利用および再コンパイルできると予想されるかなり小さなライブラリがある場合、メインプロジェクトに余分なファイルを追加したり再コンパイルしたりするのではなく、ヘッダーを他のプロジェクトと統合することで利点があります外部のlib / objファイル。