決して C/C++ の専門家ではありませんが、ヘッダー ファイルの要点は関数を宣言することであり、C/CPP ファイルは実装を定義することだと思いました。
ヘッダー ファイルの真の目的は、複数のソース ファイル間でコードを共有することです。コード管理を改善するために実装から宣言を分離するために一般的に使用されますが、これは必須ではありません。ヘッダー ファイルに依存しないコードを作成することも、ヘッダー ファイルのみで構成されるコードを作成することもできます (STL ライブラリと Boost ライブラリがその良い例です)。プリプロセッサがステートメントに遭遇すると、そのステートメントを参照されているファイルの内容に置き換え、コンパイラは完全な前処理済みコードのみを確認することに注意してください。#include
たとえば、次のファイルがあるとします。
フー.h:
#ifndef FooH
#define FooH
class Foo
{
public:
UInt32 GetNumberChannels() const;
private:
UInt32 _numberChannels;
};
#endif
Foo.cpp:
#include "Foo.h"
UInt32 Foo::GetNumberChannels() const
{
return _numberChannels;
}
Bar.cpp:
#include "Foo.h"
Foo f;
UInt32 chans = f.GetNumberChannels();
プリプロセッサは Foo.cpp と Bar.cpp を別々に解析し、コンパイラが解析する次のコードを生成します。
Foo.cpp:
class Foo
{
public:
UInt32 GetNumberChannels() const;
private:
UInt32 _numberChannels;
};
UInt32 Foo::GetNumberChannels() const
{
return _numberChannels;
}
Bar.cpp:
class Foo
{
public:
UInt32 GetNumberChannels() const;
private:
UInt32 _numberChannels;
};
Foo f;
UInt32 chans = f.GetNumberChannels();
Bar.cpp は Bar.obj にコンパイルされ、 への呼び出しへの参照が含まれていますFoo::GetNumberChannels()
。Foo.cpp は Foo.obj にコンパイルされ、の実際の実装が含まれますFoo::GetNumberChannels()
。コンパイル後、リンカーは .obj ファイルを照合し、それらをリンクして最終的な実行可能ファイルを生成します。
では、なぜヘッダーに実装があるのでしょうか?
メソッド宣言内にメソッド実装を含めることにより、暗黙的にインラインとして宣言されます (inline
明示的に使用できる実際のキーワードもあります)。コンパイラが関数をインライン化する必要があることを示すことは、関数が実際にインライン化されることを保証しないヒントにすぎません。しかし、そうであれば、インライン化された関数がどこから呼び出されても、関数の内容は、関数CALL
にジャンプして終了時に呼び出し元に戻るステートメントを生成する代わりに、呼び出しサイトに直接コピーされます。その後、コンパイラは周囲のコードを考慮して、可能であればコピーされたコードをさらに最適化できます。
const キーワードと関係がありますか?
いいえ。const
キーワードは、メソッドが実行時に呼び出されるオブジェクトの状態をメソッドが変更しないことをコンパイラに示すだけです。
CPPファイルで実装を定義するのと比較して、このようにすることの利点/ポイントは正確には何ですか?
効果的に使用すると、コンパイラは通常、より高速で最適化されたマシン コードを生成できます。