inlineの裏も外も決して簡単ではないことをよく知っています.

4713 ワード

inline関数:マクロより「安全」で、簡単な代替だけでなく、パラメータの検証があります.しかし、一般的な関数呼び出しのオーバーヘッドは必要ありません.また、コンパイラの最適化メカニズムは、コンパイラの最適化メカニズムが通常、「関数呼び出しを含まない」コードを濃縮するために使用されるため、inlineの関数もこの特権を享受します.
このような使いやすいものは、できるだけ多く使って、プログラムに花を添えなければなりません.そうしないと、簡単なinlineがあなたに与えるのは効率だけでなく、意外な悩みもあります.次は一つ一つ来ます.
まず、ターゲットコードのサイズが増加するに違いありません.これで、関数実装のボリュームをソースコードの関数呼び出しに直接埋め込みます.以下のメモリが限られているマシンでは、inlineが多すぎるとディスクのページ変更やcacheのヒット率が低くなり、逆効果になります.したがって、inlineの関数本体が小さい場合、これほど影響はありませんが、関数本体が小さい限りinlineと定義するわけではありません.後で重要な反例についてお話しします.関数を構築します.後で詳しく説明します.
次に、inlineはコマンドではなく、コンパイラに対するアドバイスであり、本当にinlingがあるかどうかはコンパイラにかかっています.
一般的に、inline関数は直接関数名にinlineキーワードを使用することでよい.またクラス内部に定義された関数もinlineの候補である.
例:
class Person
{
public:    
    const int age(){return age;} //     inline  
    /*....*/
private:
    int age;
};

最も重要なのは、
inline関数は、一般的にコンパイル中にinliningプロセスを実行する必要があるため、ヘッダファイルに定義されます.環境によっては違いますが、inliningはC++にとってコンパイル期間の動作です.
inlineは虚関数には役に立たないと思います.虚関数は実行時にしか具体的にどの関数かを決定できないからです.
再度:inlineの関数にはoutlinedバージョンがある可能性があります.例えば、次のコードなどです.
inline void f() {/*...*/}
void (*pf)() = f; //pf      f     

/*...*/
f(); // inlined
(*pf)(); //       inlined。

最後に、いよいよ話が始まります
「コンストラクション関数およびコンストラクション関数はinlineの絶好の候補者ではありません.関数体が空であっても!!」
コンストラクション関数やコンストラクション関数の間、「オブジェクトのコンストラクションやコンストラクションの間に何が起こったのか!!」とは予想できないかもしれません.
class Based
{
public:
    Based(){};
    
private:
    string Bs1, Bs2;
};

class Derived : public Based
{
public:
    Derived(){} //      !!inline!!!
private:
    string Ds1, Ds2;
    
};

Derivedのコンストラクション関数は空で、inlineは絶好の選択ですね.そこでinlineになりましたが、本当にDerivedのコンストラクション関数は何もありませんか.明らかに違う!!!
Derivedコンストラクション関数呼び出しの間、まずBasedコンストラクション関数の呼び出しと、各メンバー変数(ここではstring)のコンストラクション関数の呼び出しが行われます.ここでは2回のstringコンストラクション関数呼び出し、1回のBasedコンストラクション関数呼び出しが行われます.さらに,Basedのコンストラクション関数もinlineである場合,ここではstringコンストラクション関数呼び出しが4回,Basedコンストラクション関数呼び出しが1回である.異常なセキュリティを考慮すると、Derivedのコード量は、肉眼で見た0行ではありません!!
構造関数については,同様に逆順序で順次取り消す.
だから.ここで重要な問題があります.関数をinlinedとして宣言するのはいつですか.
通常、デバッグ時にはinlineを設定しないので、デバッグが便利です.そのため、定義した関数本体にブレークポイントを設定することができます.inlineの場合、関数呼び出しのコードが完全に置き換えられているため、できません.
何の標準的な強制的な関数もありませんinlined.よく呼び出され、関数体が小さいものをinlineにすることをお勧めします.しかし、これは決して命令ではなく、提案にすぎない.
2014,5,21
updated:
キーワードinlineはコンパイラに対する要求にすぎず、コンパイラがこの要求を処理するかどうかは関数自体の複雑さに由来する可能性があります.「コンパイラが関数の呼び出しと戻りメカニズムによる負荷が実行コストより高いと判断した場合、コンパイラはinline関数として受け入れる可能性があります.
inline関数を処理します.ステップ2:
1関数の定義を解析し,その関数の「固有の」inline性質を決定する.これは実はコンパイラが決めたことだ.
この関数が複雑すぎてinline関数にならない場合、コンパイラはこの関数をstatic関数に変換します.関数の定義を生成します.
2関数がinlineとして受け入れる場合.コンパイラは、関数が呼び出される各ポイント(拡張ポイントと呼ばれる)に、パラメータの評価と一時オブジェクトの管理をもたらします.の
なお、この関数がinlineであることを受け入れた後でも、関数の拡張点でその関数がinlineであるか否かが決定される.これは実は分かりにくいですが、すでにinlineである以上、呼び出しごとに拡張されているのではないでしょうか.実はそうではありません.例えば、次の例です.
//f関数はinline関数、
obj.f(lhs.f()+rhs.f()); 
では、「C++オブジェクトモデルを深く探る」では、inline関数が1つの式しかないと、その2番目または後続の呼び出しが拡張されないという言葉に言及しています.したがって、上記のコードは
obj.f = lhs.f + f_XXXX(&rhs);//このうちXXXXはC++コンパイラname manglingの結果である.
Inline拡張期間中、コンパイラはどうしますか?
各形式パラメータの値を単純に関数体に直接置くわけではありません.一般的には、
副作用をもたらす形式パラメータに対して、一時オブジェクトが生成されます.実際のパラメータが定数式の場合、置換前に評価の操作が完了します.
たとえば、次の関数があります.
inline int minVal(int lhs, int rhs){return (lhs > rhs)?lhs:rhs;}
では、次の3つの呼び出し方法は、コンパイラが異なる方法を採用しています.
1  ret=minVal(10,5); -->コンパイラはret=5に拡張します.
2  ret=minVal(x,y); --> ret=(x>y)に拡張しますか?x:y;
3  ret=minVal(x+y,z+y); -->ret=(t 1=x+y),(t 2=z+y),(t 1>t 2)?t1:t2;
上記のコードは形式パラメータの副作用を処理するために一時オブジェクトを生成し,inline関数定義内部自体に局所変数があれば,実際にこのinline関数はより多くの一時オブジェクトを生成する.
同様に、inline関数のローカル変数もname mangling操作が必要です.inling関数のローカル変数が関数呼び出しの閉じたセグメントに配置されることを保証しなければならないからです.
[updated] 2014.09.09
局所変数を含むinline関数についてはどうなりますか?
inline int min(int i,int j)
{
  int minVal = i
  return minVal;
}
この局所変数はinline関数の展開にどのような影響を及ぼすのでしょうか.
たとえば、次の呼び出しがあります.
{
  int local_var;
  int minVal;
...
  minVal = min(val1,val2);
}
次のように展開されます.
{
  int local_var;
  int minVal;
  int _min_lv_minVal;
  minVal = (_min_lv_minVal, val1 < val2 ? val1:val2), _min_lv_minVal;//カンマ式の値は右端の式の値です
}
inline関数の各ローカル変数は、関数呼び出しの閉じたセグメントに配置され、ユニークな名前を持つ必要があります.inline関数が単一の式で複数回拡張されている場合、拡張のたびに独自のローカル変数のセットが必要です.inline関数が複数の分離式で複数回拡張されている場合は,局所変数のセットだけで繰り返し使用できる.
inline関数のローカル変数に副作用のあるパラメータを加えると、大量の一時オブジェクトを引き起こす可能性があります.特に、単一の式で複数回拡張すると(ローカル変数は再利用できないため).
inline関数はパッケージに必要なサポートを提供し、classにパッケージされたnon-publicデータに有効にアクセスでき、defineの有効な代替品である-特にマクロのパラメータに副作用がある場合があるが、1つのinline関数が複数回呼び出されると、大量の拡張コードが生成され、プログラムのターゲットファイルのサイズが増加する.
なお,継承階層の多いctorではinline関数を用いると,拡張の複雑さが高すぎて拡張できない.