43397


  1. 行数のすくないコードはヘッダーに書き、行数が多いコードは宣言だけヘッダーに書き定義をソースファイルに場合と、ヘッダーに関数の宣言と定義を書き、ヘッダーで完結する場合との違いを知りたいです。
  2. それとヘッダーに書いた場合インラインになるとあるがインラインはその関数が使われる時に処理が埋め込まる、とそうじゃない非インラインの時の処理とでどういった違いがあるのかが知りたいです。


CC BY-SA 3.0
2018-04-18T00:22:51.810
43335
2018-04-19T15:02:19.060
2018-04-19T15:02:19.060
19110
question
c++
c++のヘッダーとソースファイルの関係性


関数の実装はソースファイル (ヘッダファイルも含めてソースファイルと呼んでいる場合もありますが、ここではそういう用語は脇に置きます)
に書き、外部に対するインターフェイスとして各種の宣言をヘッダファイルに書くのが原則です。 ヘッダファイルにはクラス定義だとか、関数の宣言のみを書くべきです。

ソースファイルひとつがひとつのコンパイル単位であり、すべてのコンパイル単位のコンパイルが完了してからリンクして実行ファイルを作るという手順を理解してください。
あるソースファイルをコンパイルしているときには、コンパイラは他のソースファイルに書かれている関数の中身を知り得ません。
ヘッダファイルを通じて関数の型を知っているだけです。

関数呼出しの替わりに関数を埋め込む (インライン化) ようにしたいとき、別のコンパイル単位にあるものは中味がわからないので埋め込むことが出来ません。
なので、同じコンパイル単位に関数の実装があるようにしなければならず、そのような特別な場合の措置としてインライン化可能関数の定義をヘッダファイルに書くことで解決するわけです。

ただし、現在の高度な言語処理系はリンク時にコンパイラに差し戻して最適化も含めたコンパイルをするようなものもあるので、そういう処理系の場合はプログラマがインライン化に配慮する必要はあまりありません。


CC BY-SA 3.0
2018-04-19T06:41:55.160
43375
2018-04-19T06:41:55.160
3364
43335
answer


C++言語のインライン関数(inline function)は、大きく2つの役割をもっています：

  * A) インライン展開 **最適化のヒント情報**
  * B) リンク時に **関数本体の重複** を許容する

A)は比較的理解しやすいですが、同時に誤解されがちな性質でもあります。インライン関数の呼び出しでは、関数本体がインライン展開されることを期待できますが、本当にインライン展開を行うか否かは
**コンパイラが判断** します。つまり、インライン関数＝「優先的にインライン展開の **候補** となりえる関数」という解釈です。

B)は少々分かりづらいのですが、複数ヘッダファイルと複数ソースファイルから構成されるC++プログラムでは重要な意味を持ちます。あるヘッダファイルに非インライン関数を定義`foo()
{...}`した場合、同ヘッダファイルを異なるソースファイルから#includeすると、各ソースファイルからコンパイルされるオブジェクトファイルそれぞれに関数`foo`の実体が含まれる状態になります。この状況でプログラム全体をリンクすると関数`foo`の実体が2回登場するため、正常なプログラムが生成されなくなります。

補足：「同一実体なら2個のうちどちらを選んでも同じでは？」と思われるかもしれませんが、コンパイラやリンカからは同一実体であることを判断するのは非常に難しいのです（もしくはコストが高い）。C++言語では実体重複が生じた時点でアウト、言い換えると「何が起きても保証しませんよ」という仕様になっています。（ODR;
One Definition Ruleとよばれる規則）

* * *

>
> 行数のすくないコードはヘッダーに書き、行数が多いコードは宣言だけヘッダーに書き定義をソースファイルに場合と、ヘッダーに関数の宣言と定義を書き、ヘッダーで完結する場合との違いを知りたいです。

行数の少ない（＝処理の少ない）関数はインライン展開の効果が大きいため、複数ソースファイルからincludeされるヘッダファイルにインライン関数として記述されるケースが多いです。

行数の多い（＝処理が多い）関数はインライン展開の効果が小さい、もしくは生成されるコードが肥大化するという負の側面が出てきてしまうため、ソースファイルに非インライン関数として記述されるケースが多いです。

上記はあくまでも慣例的なものです。ヘッダファイル中に全てをインライン関数として記述することも可能ですし、ヘッダファイルは宣言のみでソースファイル中に全ての関数定義を記述することも可能です。

（簡単のため、ここではテンプレート関数(template function)の存在を無視しています。）

* * *

>
> ヘッダーに書いた場合インラインになるとあるがインラインはその関数が使われる時に処理が埋め込まる、とそうじゃない非インラインの時の処理とでどういった違いがあるのかが知りたいです。

インライン関数・非インライン関数の違いは前述の通りです。

また「ヘッダーに書いた場合インラインになる」は少し嘘が含まれています。ヘッダファイルに書けば自動的にインライン関数になるわけではなく、一定の条件を満たす必要があります。

[code]

    // C++ヘッダファイル
    
    /* 通常関数 */
    int f1() { return 1; }  // 非インライン関数
    
    /* inlineキーワード指定あり通常関数 */
    inline
    int f2() { return 2; }  // インライン関数
    
    class Foo {
    public:
      /* クラス定義中でのメンバ関数 */
      int mf1() { return -1; }  // インライン関数
    
      int mf2();
      int mf3(); 
    };
    
    /* クラス定義外でのメンバ関数 */
    int Foo::mf2() { return -2; }  // 非インライン関数
    
    /* inlineキーワード指定ありクラス定義外でのメンバ関数 */
    inline
    int Foo::mf3() { return -3; }  // インライン関数
    
[/code]


CC BY-SA 3.0
2018-04-19T14:30:47.460
43397
2018-04-19T14:30:47.460
49
43335
answer