目次
1. インライン関数
1. C言語の「マクロ関数」を復習する
まず簡単なコードを与えます。
int Add(int left, int right) { return left + right; } int main() { int ret = 0; ret = Add(1, 2); return 0; }
アセンブリコードに移動します。
単純な加算を実現するには、Add 関数を呼び出すために多くのアセンブリ命令を実行する必要があり、関数を呼び出すために命令ジャンプを実行する必要があることがわかります (そして、スタック フレーム スペースをその関数のために開く必要があります) 。スタック領域上の関数)、Add 関数を大量に繰り返し使用すると、システム パフォーマンスの大部分が消費されます。したがって、C 言語ではプログラムの実行効率を向上させるために、同様の単純な関数の代わりにマクロを使用することがよくあります(非再帰的で短い関数に限定されることに注意してください) 。
#define Add(X,Y) ((X)+(Y)) int main() { int ret = 0; ret = Add(1, 2); return 0; }
マクロの機能は、コード ステートメントの置換と同等です。上記のコード セグメントのマクロは、Add(X,Y) の形式のステートメントを ((X)+(Y)) に置き換えます。プロセスは前処理段階で完了しています。
マクロ置換を使用した後、上記のスニペットを実行して逆アセンブリに進みます。
短い非再帰的な (頻繁に使用される) 関数の代わりにマクロを使用すると、プログラムのパフォーマンスがある程度向上することがわかります。
ただし、マクロの本質はコードの置換であるため、コードが混乱して保守が困難になる場合があり、マクロの使用自体がエラーを起こしやすくなります。C++ では、マクロの代わりにインライン関数という同様の構文メカニズムが提供されます。
2. インライン関数
inline キーワードで変更された関数はインライン関数と呼ばれ、C++ コンパイラはコンパイル時に、インライン関数を呼び出すアセンブリ命令 (call 命令)で、呼び出された関数を一連のアセンブリ命令に展開し、呼び出された関数をインライン関数で実現します。メイン関数のスタック フレーム スペース関数を呼び出す関数(マクロ置換と似ていますが、前処理段階では完了しません)、システムは呼び出された関数の関数スタック フレームを作成する必要がないため、プログラムの効率が向上します。手術。
inline を使用して Add 関数を変更する前に、前の例を使用して説明します。
inline を使用して Add 関数を変更した後:
システムは、インライン Add 関数の関数スタック フレームを作成せず、命令ジャンプも実行せず、プログラムのパフォーマンスが向上していることがわかります。(ただし、Add 関数の関数本体 (命令セグメントを含む) は、そのまま読み取り専用定数領域に格納されますが、コンパイラは関数本体内の必要な命令を、その命令セグメントに「移動」します。コンパイル中の main 関数は呼び出し命令を置き換えます)。
3. インライン関数の特徴
(1) インライン関数は、空間と時間を交換する方法です。コンパイラが関数をインライン関数として扱う場合、コンパイル段階で、関数を呼び出す呼び出し命令を、関数を完了できる命令セグメントに置き換えます。機能。
欠陥: ターゲット ファイルのサイズが増加する可能性があります (アセンブリ命令がメモリを占有し、コンパイラが呼び出し命令を一連の命令セグメントに置き換えるため、ファイル内の命令の総数が増加します)。
利点: 関数呼び出しのシステム オーバーヘッドが少なくなり、プログラムの実行効率が向上します。
(2) inline は、単純な関数と短いコード セグメントを持つ一部の非再帰関数のみを変更できます。inline を使用して複雑な関数を変更すると、コンパイラはコンパイル時に inline キーワードを自動的に無視するため、inline はコンパイラ A のみが推奨します強制するコマンドではなく、キーワードを使用します。
(3) インライン関数 (インラインで変更された関数) は、宣言と定義を分離することはお勧めしません (定義のみを使用します。定義自体も一種のレピュテーションでもあります)。分離するとリンク エラーが発生します。inline を使用しているため、関数呼び出し時に call 命令が置き換えられるため、call 命令がないとリンカは関数本体の命令部分をリンクできません。(したがって、インライン関数は通常、メイン関数と同じソースファイルで定義されます)
2. C++ の auto キーワード
1. 自動車の基本概念
C++11 では、変数の定義に auto が使用されます。autoで定義された変数の型は、変数定義の右側の値の型と初期化ステートメントの等号によって決まります。Auto は型として使用されます。 auto で宣言された変数はコンパイル時にコンパイラによって推論される必要があります。
例えば:int TestAuto() { return 10; } int main() { int a = 10; auto b = a; //变量b c d的类型由编译器根据等号右边的值自动识别 auto c = 'a'; //typeid(b).name() 是一个可以返回类型名字符串的方法 auto d = TestAuto(); cout << typeid(b).name() << endl; cout << typeid(c).name() << endl; cout << typeid(d).name() << endl; return 0; }
オブジェクト指向の複雑なプログラミングでは、 式の戻り値がどのような型になるかを判断することが難しい場合がありますが、この場合、 auto で宣言された変数を使用して式の値を受け取ることができます。C++ プログラミングでは、式の値が非常に複雑な自己定義型の値である場合がありますが、このとき、auto で宣言された変数を使用して式の値を受け取ることもできます。
たとえば、次のようなシナリオです。
#include <iostream> #include <time.h> #include <string> #include <map> using std::cout; using std::endl; int main() { std::map<std::string, std::string> m{ { "apple", "苹果" }, { "orange","橙子" }, {"pear","梨"} }; auto it = m.begin(); 用auto声名的变量it来接收表达式的返回值 while (it != m.end()) { //.... } return 0; }
上記のコードセグメントの std::map<std::string, std::string> は複合型であり、m.begin() の戻り値は auto という名前の変数で受け取ることができ、非常に便利です。 。
typedef (型の再定義) も上記のコードを簡素化できますが、typedef を使用してポインターの型を再定義し、変数を宣言する場合、 const を使用してポインターが指すメモリ空間を保護することはできません。
例えば:
typedef char* pstring; int main() { const pstring p1=NULL; return 0; }
したがって、typedef の使用も制限されますが、対照的に、auto の使用はより便利で柔軟です。
2. オート使用時の注意事項
(1) auto を使用して変数を定義する場合、変数を初期化する必要があります。コンパイル段階で、コンパイラは初期化式に従って auto の実際の型を推測する必要があります。
(2) auto は「型」宣言ではなく。コンパイラはコンパイル時に auto を変数の実際の型に置き換えます。
(3) auto を使用してポインター型を宣言する場合、auto を使用する場合と auto* を使用する場合に違いはありませんが、auto を使用して参照型を宣言する場合は、
& を追加する必要があります。
次に例を示します。int main() { int x = 10; auto a = &x; auto* b = &x; a,b的类型最终是一样的 auto& c = x; 想定义引用必须在auto后面加上& cout << typeid(a).name() << endl; cout << typeid(b).name() << endl; cout << typeid(c).name() << endl; *a = 20; *b = 30; c = 40; return 0; }
(4) 同じ行で複数の変数を宣言する場合、これらの変数は同じ型である必要があります。そうしないと、コンパイラーはエラーを報告します。これは、コンパイラーは実際には最初の型のみを推定し、その後、推定された型を使用して他の変数を定義するためです。
例えば:
void TestAuto() { auto c = 3, d = 4.0; 该行代码会编译失败,因为c和d的初始化表达式类型不同 }
3. オートが使用できない場合
1. auto は関数のパラメータとして使用できません
2. auto を直接使用して配列を宣言することはできません
例えば:
void TestAuto() { int a[] = {1,2,3}; auto b[] = {4,5,6}; 无法通过编译 }
3. C++11 の nullptr
C/C++ プログラミングを適切に実践するには、変数を宣言するときに変数に適切な初期値を与えることが最善です。そうしないと、初期化されていないポインタなどの予期しないエラーが発生する可能性があります。C 言語では、ポインタが正当なポインティングを持たない場合、基本的に次のように初期化します。
void TestPtr() { int* p1 = NULL; // …… }
NULL は実際にはマクロであり、NULL はコンパイル時に整数 0 に置き換えられるため、NULL の使用は型が厳密ではなく、極端な場合にはエラーが発生する可能性があります。
そのため、C++11では、ポインタの null 値を表す新しいキーワードとして nullptr が導入されました。
コードの堅牢性を向上させるために、ポインタの null 値を表す場合は nullptr を使用することをお勧めします。
void TestPtr() { int* p1 = nullptr; // …… }