目次
2.1 Auto はポインターおよび参照と組み合わせて使用されます
3.2 auto を直接使用して配列を宣言することはできません
4 番目、ポインターの null 値 nullptr (C++11)
1. インライン関数
1.コンセプト
インラインで修飾された関数は、インライン関数と呼ばれます。コンパイル時に、 C++ コンパイラは、インライン関数が呼び出される場所で関数を展開します。関数呼び出し用のスタック フレームを作成するオーバーヘッドはありません。インライン関数は、プログラムの効率を向上させます。動作も問題ありません。
上記の関数の前に inline キーワードを追加してインライン関数に変更すると、コンパイラはコンパイル中に関数の呼び出しを関数本体に置き換えます。見る者:
1. リリース モードでは、コンパイラによって生成されたアセンブリ コードに呼び出し Add が存在するかどうかを確認します。
2. デバッグモードでは、コンパイラを設定する必要があります。設定しないと展開されません (デバッグモードでは、コンパイラはデフォルトでコードを最適化しないため、vs2013 の設定方法は以下に示します)
2. 特徴
- インラインは、空間を時間と交換する方法です。コンパイラが関数をインライン関数として扱う場合、コンパイル段階で関数呼び出しを関数本体に置き換えます。欠点: オブジェクト ファイルが大きくなる可能性があります。利点: 呼び出しが少なくなります。オーバーヘッドを軽減し、プログラムの動作効率を向上させます。
- inline は、コンパイラに対する単なる提案です。コンパイラが異なれば、インライン実装メカニズムも異なる場合があります。一般的な提案は、関数を小さくすることです (つまり、関数はそれほど長くなく、正確なステートメントはなく、内部実装に依存します)。コンパイラの)、再帰関数や頻繁に呼び出される関数はインラインで装飾されるべきではありません。そうしないと、コンパイラはインライン機能を無視します。
- インラインでは、リンクエラーが発生するため、宣言と定義を分離することはお勧めしません。inline展開なので関数アドレスがなく、リンクが見つかりません。
3. インライン関数とマクロのメリットとデメリット
インライン関数とマクロの違いは、マクロはプリプロセッサによって置き換えられるのに対し、インライン関数はコンパイラ制御によって実装され、マクロはプリプロセス段階で置き換えられ、インライン関数はコンパイル段階で展開されます。また、inline関数は実関数ですが、使用する必要がある場合にはマクロ的に展開されるため、関数のパラメータのpushがキャンセルされ、呼び出しのオーバーヘッドが軽減されます。そのため、マクロなどの問題を心配することなく、関数呼び出しと同様にインライン関数を呼び出すことができます。
C言語では、マクロ(#define)を利用して関数を記述すると、スタックフレームを消費せずに最適化できるため、小規模な関数を頻繁に呼び出す場合に適しています。しかし、マクロには多少の欠陥があります。C++ では、インライン関数、const によって変更された定数、および enum がマクロの代わりに使用されます。
C++ でマクロを置き換える方法:
- 定数の定義は const と enum に置き換えられます。
- 短い関数定義はインライン関数に置き換えられます。
インライン関数の長所と短所?
アドバンテージ:
- インライン関数は関数であるため、関数パラメータには型があるため、パラメータ型の検出はコンパイル段階で実行され、より安全になります。
- インライン関数はコンパイル段階で拡張されており、関数呼び出しが減り、関数の動作効率が向上します。
- インライン関数はマクロ関数のようにどこにでも括弧を追加する必要がないため、実装が容易になります。
- デバッグ モードでは、デフォルトでは展開されませんが、デバッグすることも、コンパイラを設定することによって展開されるかどうかを確認することもできます。
- 副作用はありません。
欠点:
- インライン関数が使用されるほぼすべての場所が拡張され、コードが肥大化します。
マクロの長所と短所は?
(1) マクロ定数のメリットとデメリット
アドバンテージ:
- 1 つの変更とすべての変更により、エラーの可能性が減り、コードの可読性が向上します。
欠点:
- 置換は前処理ノードで実行され、型検出は実行されず、コードの安全性は低くなります。
(2) マクロ機能のメリット・デメリット
アドバンテージ:
- これは、関数呼び出しのオーバーヘッドを削減し、プログラムの動作効率を向上させる関数ではありません。
- マクロ関数は複数のステートメントをカプセル化できるため、一部のコードを節約できます。
- コードの可読性を向上させることができます。
欠点:
- マクロ関数の前処理ステージが置き換えられ、型検出は実行されず、コードのセキュリティは低くなります。
- マクロ関数はデバッグできません (プリコンパイル段階で置き換えられるため)。
- エラーが発生しやすいため、マクロ関数の各部分を括弧で囲む必要があります。
- 各マクロ関数の位置が拡張されるため、コードが拡張されます。
- マクロ関数には副作用がある可能性があります。
2. auto キーワード (C++11)
1. 自動の概要
初期の C/C++ における auto の意味は次のとおりです。auto で変更された変数は自動メモリを備えたローカル変数ですが、残念ながら誰もそれを使用していませんでした。C++11 では、標準委員会は auto にまったく新しい意味を与えました。auto はもはやストレージ型インジケータではなく、コンパイラに指示する新しい型インジケータであり、auto で宣言された変数は、次から派生したコンパイラによってコンパイルされる必要があります。期間。
int TeTestAuto()
{
return 10;
}
int main()
{
int a = 10;
auto b = a;//此时编译器会根据 a 的类型来对 b 的类型进行推导为 int
auto c = 'a';
auto d = TeTestAuto();
auto e = &a;
cout << typeid(b).name() << endl;
cout << typeid(c).name() << endl;
cout << typeid(d).name() << endl;
cout << typeid(e).name() << endl;
return 0;
}
typeid(変数名).name() は変数の型を出力できます。
注: auto を使用して変数を定義する場合は、初期化する必要があります。コンパイル段階で、コンパイラは初期化式に従って auto の実際の型を推測する必要があります。したがって、auto は「型」宣言ではなく、型が宣言されたときの「プレースホルダー」であり、コンパイラはコンパイル中に auto を変数の実際の型に置き換えます。
2.オートの利用ルール
2.1 Auto はポインターおよび参照と組み合わせて使用されます
auto を使用してポインター型を宣言する場合、 auto を使用する場合と auto* を使用する場合に違いはありませんが、 auto を使用して参照型を宣言する場合は、 & を追加する必要があります。
int x = 10;
auto a = &x;
auto* b = &x;
auto& c = x;
cout << typeid(a).name() << endl;
cout << typeid(b).name() << endl;
cout << typeid(c).name() << endl;
2.2 同じ行で複数の変数を定義する
同じ行で複数の変数を宣言する場合、これらの変数は同じ型である必要があります。そうしないと、コンパイラーはエラーを報告します。これは、コンパイラーは実際には最初の型のみを推定し、その後、推定された型で他の変数を定義するためです。
void TestAuto()
{
auto a = 1, b = 2;
auto c = 3, d = 4.0; // 该行代码会编译失败,因为c和d的初始化表达式类型不同
}
3. auto を導出できないシナリオ
3.1 auto は関数パラメータとして使用できません
// 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导
void TestAuto(auto a)
{}
3.2 auto を直接使用して配列を宣言することはできません
void TestAuto()
{
int a[] = {1,2,3};
auto b[] = {4,5,6}; //编译会失败
}
他に 2 つのシナリオがあります: (1) C++98 での auto との混乱を避けるために、C++11 では型インジケーターとしての auto の使用のみが維持されます; (2) 実際の auto の最も一般的な利点使い方としては、ラムダ式だけでなく、後述するC++11で提供される新しいforループと組み合わせて使用することになります。
3. 範囲ベースの for ループ (C++11)
1. range の構文
for ループの後の括弧は、コロン「:」によって 2 つの部分に分割されます。最初の部分は範囲内の反復に使用される変数で、2 番目の部分は反復される範囲を表します。この場合、コレクション全体を走査するときにループの範囲を指定する必要はありません。
void Test()
{
int arr[] = { 1, 2, 3, 4, 5 };
for (auto& e : arr)//要改变数组的值,需要使用引用
e *= 2;
for (auto e : arr)
cout << e << " ";
}
2. スコープの使用条件
2.1 for ループの反復範囲は明確でなければなりません
配列の場合、それは配列内の最初の要素と最後の要素の範囲です。クラスの場合、begin と end のメソッドが提供される必要があり、begin と end は for ループの反復の範囲です。
void TestFor(int array[])
{
for(auto& e : array)
cout<< e <<endl;
}
注: for のスコープが不確実であるため、上記のコードには問題があります。
4 番目、ポインターの null 値 nullptr (C++11)
1. C++98 におけるポインタの null 値
NULL は、リテラル定数 0 として、または型なしポインターの定数 (void*) として定義できます。どのような定義を採用しても、null 値のポインタを使用すると、何らかの問題が発生することは避けられません。
void f(int)
{
cout << "f(int)" << endl;
}
void f(int*)
{
cout << "f(int*)" << endl;
}
//这里函数重载,但结果都是f(int)
//C++中,NULL被定义为0,这也不知道为什么是个错误不太好
int main()
{
f(0);
f(NULL);
return 0;
}
プログラムの本来の意図は f(NULL) を通じて f(int*) 関数のポインタ版を呼び出すことですが、NULL は 0 として定義されているため、プログラムの本来の意図に反します。
C++98 では、リテラル定数 0 は整数または型なしポインター (void*) 定数のいずれかになりますが、コンパイラーはデフォルトでそれを整数定数として扱います。ポインター モードの場合は、(void *)0 に強制する必要があります。
したがって、C++11 では、NULL を nullptr に置き換えるパッチが作成されました。
知らせ:
- nullptr は C++11 で新しいキーワードとして導入されたため、nullptr を使用してポインターの null 値を表す場合、ヘッダー ファイルをインクルードする必要はありません。
- C++11 では、sizeof(nullptr) と sizeof((void*)0) は同じバイト数を占めます。
- コードの堅牢性を向上させるために、後でポインターの null 値を表すときに nullptr を使用することをお勧めします。
この記事に不備がある場合は、以下にコメントしてください。できるだけ早く修正します。