C++: 名前空間、デフォルト引数、関数のオーバーロード、参照、インライン関数

ここに画像の説明を挿入

個人ホームページ:個人ホームページ
個人コラム:「データ構造」 「C言語」「C++」


序文

このブログは C++ の知識をまとめたもので、名前空間、デフォルト パラメーター、関数のオーバーロード、参照、インライン関数について学びましょう。


1. 名前空間

次に、名前空間を導入するときに、最初に C++ で「hello world」を出力します。

#include <iostream>
using namespace std;

int main()
{
    
    
	cout << "hello world" << endl;

	return 0;
}

このうち、namespace std; の使用は名前空間の使用です。


C++ では、後で学習する変数、関数、クラスが多数あります。これらの変数、関数、クラスの名前はグローバル スコープ内に存在するため、名前の競合が発生する可能性があります。名前空間を使用する目的は、次のとおりです。識別 名前の競合や名前の汚染を避けるために、シンボルの名前をローカライズします。名前空間キーワードの出現は、この種の問題を目的としています。

たとえば、C では、変数 time を定義するときにヘッダー ファイル <time.h> をインクルードするため、名前の競合が発生します。しかし、C++ では、名前空間で変数 time を定義することでこれを回避できます。
ここに画像の説明を挿入
これにより、コンパイラはエラー C2063: 'time': は関数ではありませんという警告を発行します。
ここに画像の説明を挿入

名前空間の定義

名前空間を定義するには、名前空間キーワード、その後に名前空間の名前、そして名前空間のメンバーである {} のペアを使用する必要があります。
変数、関数、型は名前空間で定義できます。

namespace test
{
    
    
	//变量
	int time = 0;

	//函数
	int Add(int left, int right)
	{
    
    
		return left + right;
	}
	
	//类型
	struct Stu
	{
    
    
		char name[20];
		char sex[5];
		int age;
	};
}

名前空間はネストして定義できます

namespace test
{
    
    
	int time = 0;

	int Add(int left, int right)
	{
    
    
		return left + right;
	}

	struct Stu
	{
    
    
		char name[20];
		char sex[5];
		int age;
	};

	//嵌套定义test1命名空间
	namespace test1
	{
    
    
		int time1 = 1;

		int sub(int a, int b)
		{
    
    
			return a - b;
		}
	}
}

プロジェクト内で複数の同一の名前空間を定義することができ、コンパイラーは複数の同一の名前空間を 1 つの名前空間にマージします。

ここに画像の説明を挿入

ここに画像の説明を挿入

名前空間の使用

  • 名前空間名とスコープ修飾子 ( :: )
#include <iostream>
using namespace std;

namespace test
{
    
    
	int time = 0;

	int Add(int left, int right)
	{
    
    
		return left + right;
	}

	struct Stu
	{
    
    
		char name[20];
		char sex[5];
		int age;
	};

}

int main()
{
    
    
	cout << test::time << endl;

	cout << test::Add(1, 2) << endl;

	struct test::Stu s;
	return 0;
}

  • using を使用して名前空間にメンバーを導入します (部分的に導入)
#include <iostream>
using namespace std;

namespace test
{
    
    
	int time = 0;
	int a = 10;

	int Add(int left, int right)
	{
    
    
		return left + right;
	}

	struct Stu
	{
    
    
		char name[20];
		char sex[5];
		int age;
	};

}

using test::Add;
using test::a;

int main()
{
    
    
	//此处time是函数名,表示函数的地址 
	//不能using test :: time 会造成冲突,time变量只能test::time访问
	cout << time << endl;
	cout << a << endl;
	cout << Add(1, 2) << endl;

	struct test::Stu s;
	return 0;
}

  • インポートするには、使用している名前空間の名前空間名を使用します。
#include <iostream>
using namespace std;

namespace test
{
    
    
	//全部引入时,time变量与time函数会冲突
	//int time = 0;
	int a = 10;

	int Add(int left, int right)
	{
    
    
		return left + right;
	}

	struct Stu
	{
    
    
		char name[20];
		char sex[5];
		int age;
	};

}

using namespace test;

int main()
{
    
    
	cout << a << endl;
	cout << Add(1, 2) << endl;

	return 0;
}

2. デフォルトパラメータ

デフォルトパラメータの概念

デフォルトパラメータとは、関数の宣言または定義時に関数のパラメータのデフォルト値(デフォルト値)を指定するもので、関数呼び出し時に実パラメータが指定されていない場合は仮パラメータのデフォルト値が使用されます。

  • デフォルトパラメータは定義と宣言の両方に使用できません
  • デフォルト値は定数またはグローバル変数である必要があります

以下に示すように、関数 func のパラメータ a はデフォルトのパラメータです。

#include <iostream>
using namespace std;

void func(int a = 10)
{
    
    
	cout << a << endl;
}

int main()
{
    
    
	func();
	func(1);
	
	return 0;
}

結果は次のとおりです。

ここに画像の説明を挿入

デフォルトパラメータの分類

  • すべてのデフォルトパラメータ
#include <iostream>
using namespace std;

void func(int a = 10, int b = 20, int c = 30)
{
    
    
	cout << a << endl;
	cout << b << endl;
	cout << c << endl;

}

int main()
{
    
    
	func();
	cout << endl;

	func(1);
	cout << endl;

	func(1, 2);
	cout << endl;

	return 0;
}

結果は次のとおりです。

ここに画像の説明を挿入

  • 準デフォルトパラメータ

  • 準デフォルトパラメータは右から左に順番に指定する必要があり、間隔をあけて指定することはできません

#include <iostream>
using namespace std;


void func(int a, int b = 1, int c = 2)
{
    
    
	cout << a << endl;
	cout << b << endl;
	cout << c << endl;
}

int main()
{
    
    
	func(0);
	cout << endl;


	func(0, 10);
	cout << endl;

	func(0,10,20);
	cout << endl;

	return 0;
}

ここに画像の説明を挿入

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

関数のオーバーロードの概念

関数のオーバーロード: これは関数の特殊なケースです。C++ では、同じ名前を持つ複数の関数が同じスコープ内で同様の関数を宣言できます。同じ名前を持つこれらの関数の仮パラメータ リスト(パラメータの数、型、型の順序) は次のとおりです。関数の実装は、異なるデータ型の問題に似ています。

  • 同じ名前を持つ 2 つの関数の戻り値が異なるだけの場合、それらは関数のオーバーロードにはなりません。

さまざまなパラメータのタイプ

#include <iostream>
using namespace std;

int Add(int a, int b)
{
    
    
	return a + b;
}

double Add(double a, double b)
{
    
    
	return a + b;
}

int main()
{
    
    
	cout << Add(1, 2) << endl;
	cout << Add(1.1, 2.2) << endl;


	return 0;
}

ここに画像の説明を挿入


パラメータの数が違う

#include <iostream>
using namespace std;

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

void func(int i, int j)
{
    
    
	cout << "void func(int i, int j)" << endl;
}

//注意无参函数的重载函数,不能有全缺省类型的函数,会造成函数调用的二义性
//void func(int i = 1, int j = 1, int k = 1)
//{
    
    
//	cout << "void func(int i = 1, int j = 1, int k = 1)" << endl;
//}

int main()
{
    
    
	func();
	func(1, 1);

	return 0;
}

ここに画像の説明を挿入


パラメータの型の順序が異なります

#include <iostream>
using namespace std;

void func(int i, double j)
{
    
    
	cout << "void func(int i, double j)" << endl;
}

void func(double i, int j)
{
    
    
	cout << "void func(double i, int j)" << endl;
}

int main()
{
    
    
	func(1, 1.1);
	func(1.1, 1);

	return 0;
}

ここに画像の説明を挿入

4. 引用

参考コンセプト

参照は新しく定義された変数ではなく、既存の変数の別名です (人にニックネームを付けるのと似ています)。コンパイラは参照変数用のメモリ空間をオープンせず、参照変数と同じ空間を共有します。を指します。

タイプ + & + 参照オブジェクト名 = 参照エンティティ

#include <iostream>
using namespace std;

int main()
{
    
    
	int i = 0;

//定义引用类型
// 类型  & 对象名  引用实体
	int  &  ri  =  i;
	
	cout << &i << endl;
	cout << &ri << endl;
	return 0;
}

ここに画像の説明を挿入


引用特性

  • 参照は定義時に初期化する必要があります
  • 変数には複数の参照を含めることができます
  • 参照がエンティティを参照すると、別のエンティティを参照できなくなります
  • 参照された権限はパン、ズームアウトできますが、ズームインはできません

参照は定義時に初期化する必要があります

ここに画像の説明を挿入

ここに画像の説明を挿入


変数には複数の参照を含めることができます

#include <iostream>
using namespace std;

int main()
{
    
    
	int i = 0;

	int& ri = i;
	int& rri = i;
	int& rrri = i;

	cout << &i << endl;
	cout << &ri << endl;
	cout << &rri << endl;
	return 0;
}

ここに画像の説明を挿入


参照がエンティティを参照すると、別のエンティティを参照できなくなります

ここに画像の説明を挿入
ここに画像の説明を挿入


参照された権限はパン、ズームアウトできますが、ズームインはできません

ここに画像の説明を挿入
ここに画像の説明を挿入


参照された使用シナリオ

1. パラメータとして
リファレンスの戻り値はポインタの戻り値に似ており、実際のパラメータは仮パラメータを通じて直接変更できますが、リファレンスの方が使いやすいです。

#include <iostream>
using namespace std;

void swap(int& i, int& j)
{
    
    
	int tmp = i;
	i = j;
	j = tmp;
}

int main()
{
    
    
	int i = 0;
	int j = 10;

	cout << " i = " << i << " j = " << j << endl;
	swap(i, j);
	cout << " i = " << i << " j = " << j << endl;

	return 0;
}

ここに画像の説明を挿入


2. 戻り値を実行する

  • 関数が関数のスコープ外でリターンする場合、リターン オブジェクトが存在する場合は参照で返すことができますが、すでにシステムに返されている場合は値で返す必要があります。

次のようになります: (1) 、 (2) 、 (3) の結果は何ですか?

int& Add(int a, int b)
{
    
    
	int c = a + b;

	return c;
}

int main()
{
    
    
	int& ret = Add(1, 2);
	cout << "Add(1,2) = " << ret << endl;// (1)
	Add(3, 4);

	cout << "Add(1,2) = " << ret << endl;// (2)
	cout << "Add(1,2) = " << ret << endl;// (3)

	return 0;
}

ここに画像の説明を挿入
なぜこのような結果になったのでしょうか? retは関数関数の戻り値を一度しか受け付けないのでしょうか?
これは、変数 c のライフサイクルが関数スタック フレームの作成とともに作成され、スタック フレームが破棄されるときに破棄されるためです (スタック フレームの破棄は実際にはスペース、つまり C 変数スペースの値を破棄しません)変更されません。esp ebp へのポイントが変更されただけです)。関数スタックフレームの作成と破棄
ret は変数 C のスペースも指します。ret の値が初めて出力され、変数 C space の値は変更されず、3 を出力できます。2回目にAdd(3,4)関数を呼び出すときは、メイン関数のスタックフレーム上に他のスタックフレームが存在しないため、Add(3,4)のスタックフレーム位置とスタックフレームがAdd(1,2) の位置 同様にすると、ret が指す変数 C の空間が、今度は変数 C の空間で上書きされ、変数 space の値は 7 になります。ret の値が 2 回目に出力されるとき、ret の値は 7 になります。このとき、 cout << "Add(1,2) = " << ret << endl; も関数呼び出しです。変数 C の空間を上書きすると、変数 C の空間はランダムな値になるため、3 回目に出力される ret の値はランダムな値になります。


リファレンスとポインタの違い

文法上の概念では、参照は別名であり、独立した空間はありません。しかし、基礎となる実装では、参照はポインター メソッドに従って実装されるため、実際には参照にスペースがあります。

#include <iostream>
using namespace std;

int main()
{
    
    
	int i = 0;

	int& ri = i;
	int* pi = &i;

	return 0;
}

ここに画像の説明を挿入
参照は、基礎となる実装という点ではポインターと何ら変わらないことがわかります。

参照とポインタに違いはありますか?

  • 参照は概念的に変数の別名を定義し、ポインターは変数のアドレスを格納します。
  • 参照は定義時に初期化する必要があり、ポインタは必要ありません
  • 初期化中に参照がエンティティを参照した後は、他のエンティティを参照することはできず、ポインタはいつでも同じタイプのエンティティを指すことができます。
  • NULL 参照はありませんが、NULL ポインター (C++ では nullptr) はあります。
  • sizeof では意味が異なります。参照結果は参照型のサイズであり、ポインタは常にアドレス空間が占有するバイト数 (4 / 8) になります。
  • 参照の値の変更は、参照されるエンティティ自体の値の変更を意味し、ポインターの値の変更は別の変数を指します。
  • マルチレベル ポインタ、マルチレベル参照なし
  • エンティティにアクセスするにはさまざまな方法がありますが、ポインタは逆参照する必要があり、リファレンス コンパイラがそれを自動的に処理します。
  • 参照はポインタより安全です

5. インライン関数

C 言語では、プログラムの効率を向上させたい場合があり、プログラムの効率を向上させるために、いくつかの単純な関数をマクロの形式で記述します。しかし、マクロ関数の場合、デバッグして問題が発生した場合にそれを確認するのは簡単ではありません。そこで C++ は、この状況に対応するためにインライン関数の概念を提案しました。

inline修飾された関数は、インライン関数と呼ばれます。コンパイル時に、C++ コンパイラは、インライン関数が呼び出される場所でその関数を展開します。スタック フレームを作成するための関数呼び出しのオーバーヘッドはなく、インライン関数は、インライン関数を置き換えることができます。マクロ機能。
次のように: Add 関数がインラインで変更された後、呼び出されるときに呼び出し命令はなく、直接追加されます。

#include <iostream>
using namespace std;

inline int Add(int a, int b)
{
    
    
	return a + b;
}

int main()
{
    
    
	int ret = 0;

	ret = Add(1, 2);
	return 0;
}

ここに画像の説明を挿入

特性

  • インラインは、空間を時間と交換する方法です。コンパイラが関数をインライン関数として扱う場合、コンパイル段階で関数呼び出しを関数本体に置き換えます。欠点: オブジェクト ファイルのサイズが増加する可能性があります。
  • インラインはコンパイラに対する単なる提案です。コンパイラが異なれば、インラインの実装メカニズムも異なります。一般的な提案は、規模が小さく、再帰的ではなく、インラインで頻繁に呼び出される関数を変更することです。そうしないと、コンパイラがインライン機能を無視する可能性があります。
  • インラインでは宣言と定義を分離することは推奨されません。インラインは展開され、関数アドレスがないため、分離するとリンク エラーが発生します。リンク段階で、コンパイラはシンボル テーブルに従って関数アドレスを見つけることができません。

要約する

上記は、C++ の名前空間、デフォルト パラメーター、関数のオーバーロード、参照、およびインライン関数について私がまとめたものです。ご支援ありがとうございます!
ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/li209779/article/details/132482438