99

決して C/C++ の専門家ではありませんが、ヘッダー ファイルの要点は関数を宣言することであり、C/CPP ファイルは実装を定義することだと思いました。

ただし、今夜いくつかの C++ コードを確認したところ、クラスのヘッダー ファイルにこれが見つかりました...

public:
    UInt32 GetNumberChannels() const { return _numberChannels; } // <-- Huh??

private:
    UInt32 _numberChannels;

では、なぜヘッダーに実装があるのでしょうか? constキーワードと関係ありますか?それはクラスメソッドをインライン化しますか?CPPファイルで実装を定義するのと比較して、このようにすることの利点/ポイントは正確には何ですか?

4

7 に答える 7

161

決して 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ファイルで実装を定義するのと比較して、このようにすることの利点/ポイントは正確には何ですか?

効果的に使用すると、コンパイラは通常、より高速で最適化されたマシン コードを生成できます。

于 2013-01-25T08:22:59.507 に答える
46

ヘッダー ファイルに関数を実装することは完全に有効です。これに関する唯一の問題は、1 つの定義ルールに違反していることです。つまり、他の複数のファイルのヘッダーを含めると、コンパイラ エラーが発生します。

ただし、例外が 1 つあります。関数をインラインとして宣言すると、1 つの定義規則から除外されます。クラス定義内で定義されたメンバー関数は暗黙的にインライン化されるため、これがここで起こっていることです。

インライン自体は、関数がインライン化の適切な候補である可能性があるというコンパイラへのヒントです。つまり、単純な関数呼び出しではなく、その呼び出しを関数の定義に展開します。これは、生成されたファイルのサイズをより高速なコードと引き換えにする最適化です。最新のコンパイラでは、関数にこのインライン ヒントを提供することは、1 つの定義規則に与える影響を除いて、ほとんど無視されます。inlineまた、コンパイラは、 (明示的または暗黙的に)宣言されていない場合でも、適切と思われる関数をいつでも自由にインライン化できます。

あなたの例ではconst、引数リストの後に を使用すると、メンバー関数が呼び出されたオブジェクトを変更しないことが通知されます。実際には、これは が指すオブジェクトthis、および拡張によりすべてのクラス メンバーが と見なされることを意味しますconst。つまり、それらを変更しようとすると、コンパイル時エラーが発生します。

于 2013-01-25T07:56:32.327 に答える
9

クラス宣言内で定義されたメンバー関数であるため、暗黙的に宣言されます。 これは、コンパイラインライン化する必要があるという意味ではありませんが、1 つの定義ルールを破らないことを意味します。*とはまったく関係ありません。また、関数の長さや複雑さとは関係ありません。inlineconst

非メンバー関数の場合は、次のように明示的に宣言する必要がありますinline

inline void foo() { std::cout << "foo!\n"; }

メンバー関数の最後についてはこちらをご覧ください。const

于 2013-01-25T07:54:58.947 に答える
4

プレーンな C でも、コードをヘッダー ファイルに入れることができます。そうしないと、static同じヘッダーを含む複数の .c ファイルによって「複数定義された関数」エラーが発生します。

プリプロセッサはインクルード ファイルをテキストでインクルードするため、インクルード ファイル内のコードはソース ファイルの一部になります (少なくともコンパイラの観点からは)。

C++ の設計者は、適切なデータ隠蔽機能を備えたオブジェクト指向プログラミングを可能にしたいと考えていたため、多くのゲッター関数とセッター関数が見られることを期待していました。彼らは、不当なパフォーマンスの低下を望んでいませんでした。そのため、ゲッターとセッターをヘッダーで宣言できるだけでなく、実際に実装してインライン化できるように C++ を設計しました。あなたが示したその関数はゲッターであり、そのC++コードがコンパイルされると、関数呼び出しはありません。その値を取得するコードは、その場でコンパイルされます。

ヘッダー ファイルとソース ファイルの区別がなく、コンパイラが理解する実際の "モジュール" だけを持つコンピューター言語を作成することは可能です。(C++ はそうしませんでした。ソース ファイルとテキストでインクルードされたヘッダー ファイルの成功した C モデルの上に構築しただけです。) ソース ファイルがモジュールである場合、コンパイラがモジュールからコードを引き出して、そのコードをインライン化します。しかし、C++ の方法の方が実装が簡単です。

于 2013-01-25T08:02:23.093 に答える
2

私の知る限り、ヘッダー ファイル内に安全に実装できる 2 種類のメソッドがあります。

  • インライン メソッド - それらの実装は、それらが使用される場所にコピーされるため、二重定義リンカ エラーの問題はありません。
  • テンプレート メソッド - テンプレートのインスタンス化の瞬間 (たとえば、誰かがテンプレートの代わりに型を入力したとき) に実際にコンパイルされるため、ここでも二重定義の問題が発生する可能性はありません。

あなたの例は最初のケースに当てはまると思います。

于 2013-01-25T07:59:12.903 に答える
0

コードをコンパイルしたことがあれば、クラス ヘッダー ファイルに実装を保持することで機能します。このconstキーワードは、メンバーを変更しないことを保証し、メソッド呼び出しの間、インスタンスを不変に保ちます。

于 2013-01-25T08:24:45.847 に答える