C++ の基本入門 (中): 関数のオーバーロード、参照、インライン関数

目次

導入

1. 関数のオーバーロード

1.1 コンセプト

1.2 例

①パラメータの種類の違い

②パラメータの数が違う

③パラメータの型の順序が違う

知らせ

1.3 原則

例えば

知らせ

二、 引用

2.1 参照される宣言と定義

2.2 リファレンスの特徴

2.3 参照とポインタの違い

2.4 共通の参照先

2.4.1 よく引用される機能

2.5 参考アプリケーション

3. インライン関数

3.1 インライン関数の宣言方法

3.2 インライン関数の利点

3.3 インライン関数に関する考慮事項


導入

このシリーズは、初心者向けに包括的でわかりやすいC++ 入門ガイドを提供することを目的としています。C++ キーワードから始めて、名前空間、入出力、関数機能、キーワード、範囲ベースの for ループ、などのC++11の新機能など、C++ のさまざまな側面を徐々に調べていきます各トピックには、この知識をより深く理解し、適用するのに役立つ簡潔な説明とサンプル コードが含まれています。autonullptr

プログラミングを始めたばかりの場合でも、別のプログラミング言語から C++ に切り替えたい場合でも、このガイドは C++ の強固な基盤を構築するのに役立ちます。このシリーズを学習することで、C++ の中核概念と共通機能を習得し、その後の学習とプロジェクト開発のための強固な基盤を築きます。

1. 関数のオーバーロード

1.1 コンセプト

関数のオーバーロードは、 C++ プログラミングにおいて非常に便利な機能です。これにより、同じスコープ内で同じ名前の複数の関数を宣言できますが、これらの関数のパラメーターの型、パラメーターの数、またはパラメーターの順序が異なります。関数のオーバーロードを通じて、同じ関数名を使用して異なる操作を表すことができるため、コードの柔軟性と可読性が向上します。

1.2 例

①パラメータの種類の違い

int Add(int left, int right)
{
    cout << "int Add(int left, int right)" << endl;
    return left + right;
}


double Add(double left, double right)
{
    cout << "double Add(double left, double right)" << endl;
    return left + right;
}

②パラメータの数が違う

void fun(int a)
{
    cout << "fun(int a)" << endl;
}

void fun()
{
    cout << "fun()" << endl;
}

③パラメータの型の順序が違う

void fun(int a, double b)
{
    cout << "fun(int a, double b)" << endl;
}

void fun(double a, int b)
{
    cout << "fun(double a, int b)" << endl;
}

これらの関数を呼び出すとき、ユーザーは要件に従って関数を選択し関数プロトタイプに従って実際のパラメータを渡します。コンパイラは、関数に渡されるパラメータのタイプと数に従って、呼び出す適切な関数を選択します。このようにして、同じ関数名を使用して異なる操作を表すことができます。

知らせ

関数のオーバーロードの有効性は、戻り値の型に関係なくC++ では、関数の戻り型は関数のオーバーロードの選択プロセスに影響しません。これは、関数の呼び出し時に関数の戻り値の型が関数のオーバーロードのあいまいさを解決するために使用されていないためです。

関数のオーバーロードの解決策は、関数に渡されるパラメーターの型と数に応じて、コンパイラーが呼び出す正しい関数を選択することです。コンパイラは関数を呼び出すときに、実際に渡されたパラメータに基づいて一致する関数宣言を探します。一致するパラメーター リストを持つ関数宣言が見つかった場合、これらの関数の戻り値の型が同じかどうかに関係なく、対応する関数が呼び出されます。

1.3 原則

  1. 名前マングリング (名前マングリング) : C++ では、関数のオーバーロードには関数名の区別が含まれます。関数のオーバーロードは同じ関数名を持つ可能性があるため、コンパイル中にこれらの関数を区別するために、C++ コンパイラーは関数名を変更します。これは名前マングリング (名前マングリング) とも呼ばれます。名前マングリングとは、関数名とパラメーター リスト情報を一意の文字列にエンコードすることで、コンパイル後に異なる関数名が生成されます。このように、同じ関数名でもコンパイル後は異なる関数名になるため、関数のオーバーロードを区別することができます。

  2. パラメータのマッチング: 関数呼び出しを行うとき、C++ コンパイラは、渡されたパラメータのタイプと数に応じて適切な関数をマッチングします。コンパイラは、関数に渡された実際のパラメータの型と仮パラメータの型を照合して、一致する関数宣言を見つけます。パラメータのマッチングは実行時ではなくコンパイル時に行われます。

  3. 関数呼び出しの解決: コンパイラーが適切な関数宣言と一致すると、対応する関数呼び出しを生成します。名前のマングリングにより、関数の実際の呼び出し名は、ソース コード内の関数名とまったく同じではない場合があります。

例えば

int add(int a, int b);
float add(float a, float b);

コンパイラは、add(int, int)関数名を"_Z3addii"で修飾し、add(float, float)関数名を"_Z3addff"で修飾する場合があります。そのため、ソースコード内で呼び出される関数名と実際に生成される関数名は異なる場合があります。

int sum1 = add(5, 10);          // 实际调用的函数名可能为"_Z3addii"
float sum2 = add(3.14f, 2.71f); // 实际调用的函数名可能为"_Z3addff"

知らせ

名前装飾の具体的な方法とルールはコンパイラごとに異なる場合があり、これにより、コンパイラごとに関数のオーバーロードを処理する方法にもわずかな違いが生じます。下位互換性を維持するために、C++ では、extern "C"C++ コードで C 言語の関数名の装飾を宣言するメカニズムが提供されています。

二、 引用

C++ では、参照は変数にエイリアスを付けるためのメカニズムです。参照を使用すると、新しい記憶域を作成するのではなく、名前によって既存の変数の値にアクセスできます。リファレンスは、より直感的で簡潔な構文を C++ に導入し、変数を操作するためのより安全で効率的な方法を提供します。

2.1 参照される宣言と定義

参照は&シンボルを使用して宣言されます。参照を宣言するときは、参照のターゲット、つまり参照される変数に初期化する必要があります。参照が初期化されると、その参照は常にその変数を参照し、別の変数に再バインドすることはできません

型&参照変数名(オブジェクト名) = 参照エンティティ;

int num = 10;
int& ref = num;  // 引用声明和初始化

コードの最初の行は、値 10 を格納するためにスペースがスペース内に開かれ、その名前がnumであることを示しています。コードの 2 行目は、このスペースに別の名前refを与えることを示しています。どちらの名前もこのスペースで操作できます。効果は同じです

注:参照型は参照されるエンティティと同じ型である必要があります。もちろん例外もありますが、これについては後で説明します。

2.2 リファレンスの特徴

  1. Alias : 参照は変数の別名を提供します。参照により、異なる名前を使用して同じ変数にアクセスできます。

  2. null 参照は禁止: C++ では、参照を宣言するときに参照を初期化する必要があり、一度初期化されると、参照するオブジェクトは変更できません。

  3. 独立したアドレスなし: 参照自体はメモリ空間を占有せず、変数の単なるエイリアスです。そのため、参照用のアドレスを取得できません。

  4. パラメータの受け渡し: 参照は、関数のパラメータの受け渡しに特に役立ちます。参照によってパラメータを渡し、関数内のパラメータの変更による元の変数の効果を実現できます。

2.3 参照とポインタの違い

参照とポインタは、C++ における 2 つの異なる変数エイリアシング メカニズムであり、それらの間には次のような主な違いがあります。

  1. 初期化と再バインド: 参照は宣言時に初期化する必要があり、一度初期化すると他の変数に再バインドすることはできません。ポインターは初期化できませんが、他の変数を再度指すことができます。

  2. Null 参照: C++ では、Null 参照の作成は許可されていません。つまり、参照は既存のオブジェクトにバインドされている必要があります。ただし、ポインタは何も指すことはできません (nullptr)。

  3. 構文: 参照は&シンボルを使用して宣言され、ポインターは*シンボルを使用して宣言されます。

  4. メモリ占有: 参照自体は追加のメモリ空間を占有しませんが、ポインタはターゲット オブジェクトのアドレスを格納する必要があり、一定量のメモリ空間を占有します。

2.4 共通の参照先

定数参照 (const 参照) は、参照を通じてアクセスされるオブジェクトの変更を制限するために C++ で使用される特殊なタイプの参照です。参照を const として宣言することで、参照が指すオブジェクトが読み取り専用であり、参照を介したオブジェクトの変更が許可されていないことをコンパイラに伝えます

定数参照は、参照宣言にキーワードを追加することで実装されますconst。その宣言形式は次のとおりです。

const T& ref = 引用实体;
//T是要引用的对象类型

2.4.1 よく引用される機能

  1. 読み取り専用アクセス: 定数参照では、参照されたオブジェクトの値を読み取ることのみが可能で、変更することはできません。定数参照を通じてオブジェクトを変更しようとすると、コンパイル エラーが発生します。

  2. 初期化要件: 定数参照は宣言時に初期化する必要があり、同じ型の 1 つの変数または定数のみを参照できます。

  3. 一時オブジェクト: 定数参照は一時オブジェクト (右辺値) にバインドできますが、一時オブジェクトは式の終了後に破棄されるため、一時オブジェクトの値のみにアクセスでき、変更できません。

   3 番目のポイントの例

int& ref = 10; //错误 10为右值,不能通过ref进行修改
const int& ref = 10; //正确 此时ref为常引用不可修改内容

2.5 参考アプリケーション

1. 関数パラメータの受け渡し: パラメータの参照渡しにより、関数内で元の変数を変更することができます。ポインターの使用を簡素化します。

void increment(int& x) {
    x++;
}

int num = 10;
increment(num);
// 现在num的值为11

2. 戻り値:この関数は、一時変数の作成を回避して、参照によって結果を返すことができます。

int& findLargest(int& a, int& b) {
    return (a > b) ? a : b;
}

int a = 10, b = 15;
int& largest = findLargest(a, b);
largest = 20;
// 现在b的值为20,即largest为b的引用

3. コピーの回避: 参照によりオブジェクトのコピーが回避され、コードの実行効率が向上します。

void processLargeObject(const LargeObject& obj) {
    // 处理大对象的操作,不需要复制对象
}

ここでは定数参照が使用されています。これは、オブジェクトが関数内で変更されないことを意味します。

知らせ

2 番目のポイントで値を返すときは、一時変数参照を返さないように注意してください

int& fun()
{
    int a = 10;
    return a;
}

例えば、このコードでは変数 a は関数実行直後に破棄されますが、この時点でもリファレンス通りエイリアスとして使用されていると、結果が不確実になる不正な操作となります。

3. インライン関数

インライン関数(インライン関数)は、C++ の関数最適化メカニズムであり、コンパイラーのコンパイル時に、関数呼び出しを介して実行するのではなく、関数本体のコードを呼び出し側サイトに直接挿入するために使用されます。そうすることで、関数呼び出しのオーバーヘッドが回避され、コードの実行効率が向上します。

3.1 インライン関数の宣言方法

C++ では、inlineキーワードを使用してインライン関数を宣言します。通常、inlineクラス内で定義されたメンバー関数でキーワードを使用して、メンバー関数をインライン宣言できます。クラスの外で定義された関数の場合、inline関数の宣言と定義でキーワードを使用できます。

クラス内で定義されたインライン関数:

class MyClass {
public:
    inline void myFunction() {
        // 函数体代码
    }
};

クラス外で定義されたインライン関数

// 在函数声明处使用inline关键字
inline void myFunction();

// 在函数定义处使用inline关键字
inline void myFunction() {
    // 函数体代码
}

3.2 インライン関数の利点

  1. 関数呼び出しのオーバーヘッドの削減: 関数が呼び出されると、現在の実行状態がスタックに保存され、呼び出された関数にジャンプし、実行後に呼び出しポイントに戻ります。このプロセスには、複数のスタック フレームの作成と破棄が含まれます。 。インライン関数のコードは呼び出しポイントに直接挿入されるため、これらの追加のオーバーヘッドが回避され、コードの実行効率が向上します。

  2. メモリ スペースの節約: インライン関数のコードは各呼び出しポイントに直接挿入され、メモリ内に関数のコピーは生成されないため、コード スペースが節約されます。

3.3 インライン関数に関する考慮事項

  1. インライン関数の複雑さ: インライン関数は通常、短い関数に適しています。関数本体が複雑すぎてコードが肥大化し、パフォーマンスが低下するためです。コンパイラは、インライン化された複雑な関数のインライン化を拒否する場合があります。

  2. コンパイラーの決定: キーワードの使用は、inlineインライン化に関するコンパイラーへの単なる提案であり、関数をインライン関数として使用するかどうかは最終的にコンパイラーが決定します。コンパイラはinline、特に過度に複雑な関数や再帰的な関数の場合、キーワードを無視することがあります。

  3. インラインでは、リンクエラーが発生するため、宣言と定義を分離することはお勧めしません。

  4. インライン関数の乱用を避ける: インライン関数はパフォーマンス上の利点をもたらしますが、すべての関数がインライン化に適しているわけではありません。インライン関数のコードは各呼び出しサイトにコピーされるため、関数本体が大きすぎたり、頻繁に呼び出されたりすると、コードが肥大化し、かえってパフォーマンスが低下する可能性があります。

おすすめ

転載: blog.csdn.net/weixin_57082854/article/details/132069044