技術共有

初めての C 入門 (名前空間、デフォルトパラメータ、関数のオーバーロード)

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

1. 名前空間

1. 名前空間の意味

C/C++ には、後で学習する変数、関数、クラスが多数あり、これらの変数、関数、クラスの名前は世界中に存在します。

ローカル スコープでは、多くの競合が発生する可能性があります。名前空間を使用する目的は、名前付けを避けるために識別子の名前をローカライズすることです。

競合または名前汚染。namespac キーワードがこの問題に対処するようです。

C 言語プロジェクトにおける次のプログラムのような名前の競合は、一般的な問題です。C++ では、それらをより適切に解決するために namespac が導入されています。

このような問題: (設定した変数 rand が stdlib.h の rand 関数と競合するため、エラーが報告されます)

#include <stdio.h>
#include <stdlib.h>
int rand = 10;
int main()
{
	// 编译报错:error C2365: “rand”: 重定义;以前的定义是“函数”
	printf("%dn", rand);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2. 名前空間の定義

1. 名前空間を定義するには、namespace キーワードを使用し、その後に名前空間の名前を入力し、{} のペアを接続する必要があります。ここで、{}

つまり、名前空間のメンバーです。名前空間には変数/関数/型などを定義できます。({} の後に「;」が続かないことに注意してください)

2. ネームスペースの本質はドメインを定義することです。このドメインはグローバル ドメインから独立しているため、次のようになります。

上記のランドはもはや競合していません。

#include<iostream>
#include <stdio.h>
#include <stdlib.h>
namespace tmp
{
	int rand = 0;
}
int main()
{
	printf("%dn", rand);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

3. C++ のドメインには、関数ローカル ドメイン、グローバル ドメイン、名前空間ドメイン、およびクラス ドメインが含まれます。ドメインは、変数/関数/を見つけるためのコンパイル時の構文に影響します。

タイプのオリジン (宣言または定義) のロジックは、ドメイン分離により、名前の競合が解決されます。ローカル ドメインとグローバル ドメインに影響を与えるだけでなく、

検索ロジックのコンパイルは変数宣言サイクルにも影響します。名前空間ドメインとクラス ドメインは変数宣言サイクルには影響しません。

4. 名前空間はグローバルにのみ定義できますが、もちろんネストすることもできます。

#include<iostream>
#include <stdio.h>
#include <stdlib.h>
namespace tmp
{
	int rand = 0;
	namespace tmp1
	{
		int Add(int left, int right)
		{
			return left + right;
		}
		struct Node
		{
			struct Node* next;
			int val;
		};
	}

}
int main()
{
	printf("%dn", rand);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

5. プロジェクト内の複数のファイルに定義されている同じ名前の名前空間は、1 つの名前空間とみなされ、競合しません。

test.c ファイル

#include<iostream>
#include <stdio.h>
#include <stdlib.h>
#include"test.h"
namespace tmp
{
	int rand = 0;
	namespace tmp1
	{
		int Add(int left, int right)
		{
			return left + right;
		}
		struct Node
		{
			struct Node* next;
			int val;
		};
	}

}
int main()
{
	printf("%dn", tmp::d);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

test.h ファイル

#pragma once
namespace tmp
{
	int d = 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5

tmp 空間の d 変数が .c ファイルで使用できることがわかります。

6. C++ 標準ライブラリは、std (standard) という名前空間に配置されます。

7. グローバル ドメインで変数を使用したい場合は、次のようにすることができます。

int a = 3;
int main()
{
	int a = 0;
	printf("%dn", ::a);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

このように、printf は最初に 3 を出力します。

3. 名前空間の使用

変数の宣言/定義を検索するためにコンパイルする場合、デフォルトでは、名前空間内ではなく、ローカルまたはグローバルのみが検索されます。それで

次のプログラムはコンパイルされてエラーを報告します。

namespace tmp
{
	int d = 0;
	namespace tmp1
	{
		int Add(int left, int right)
		{
			return left + right;
		}
		struct Node
		{
			struct Node* next;
			int val;
		};
	}
}
int main()
{
	int a = 0;
	printf("%dn", d);//未定义标识符d
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

したがって、名前空間で定義された変数/関数を使用する必要があります。方法は 3 つあります。

1. 名前空間アクセスを指定する プロジェクトではこの方法が推奨されます。

tmp::d
  • 1

2. を使用すると、名前空間のメンバーが展開されます。このアプローチは、頻繁にアクセスされる、競合のないプロジェクトのメンバーに推奨されます。

namespace tmp
{
	int d = 0;
	namespace tmp1
	{
		int Add(int left, int right)
		{
			return left + right;
		}
		struct Node
		{
			struct Node* next;
			int val;
		};
	}
}
using tmp::d;
int main()
{
	int a = 0;
	printf("%dn", d);//未定义标识符d
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

3. ネームスペース内のすべてのメンバーを展開します。競合のリスクが高いため、このプロジェクトは推奨されません。便宜上、毎日の練習プログラムが推奨されます。

namespace tmp
{
	int d = 0;
	namespace tmp1
	{
		int Add(int left, int right)
		{
			return left + right;
		}
		struct Node
		{
			struct Node* next;
			int val;
		};
	}
}
using namespace tmp;
int main()
{
	int a = 0;
	printf("%dn", d);//未定义标识符d
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

2. 入力と出力

1.Input Output Streamの略で、標準入出力を定義する標準入出力ストリームライブラリです。

アウトオブジェクト。

2. std::cin は、istream クラスのオブジェクトであり、主にナロー文字 (char 型) の標準出力を対象としています。

流入。

3. std::cout は、ostream クラスのオブジェクトであり、主にナロー文字の標準出力ストリームを対象としています。

4. std::endl は関数です。ストリームが出力に挿入されると、改行文字を挿入してバッファをリフレッシュするのと同じになります。

5. &lt;&lt; はストリーム挿入演算子、&gt;&gt; はストリーム抽出演算子です。 (C 言語では、左シフト/右シフトなどのビット単位の演算を実行するためにこれら 2 つの演算子も使用します)

6. 入出力には C++ を使用すると便利です。 printf/scanf 入出力** のように形式を手動で指定する必要はありません。

出力では変数の型を自動的に識別できます(基本的に、これは関数のオーバーロードによって実現されます)。実際、最も重要なことは、C++ ストリームがカスタマイズをより適切にサポートできることです。

型オブジェクトの入力と出力。

#include<iostream>
#include <stdio.h>
#include <stdlib.h>
namespace tmp
{
	int a = 0;
	double b = 0.0;
	char c = '0';
	namespace tmp1
	{
		int Add(int left, int right)
		{
			return left + right;
		}
		struct Node
		{
			struct Node* next;
			int val;
		};
	}
}
using namespace tmp;
using namespace std;
int main()
{
	cin >> a >> b >> c;
	cout << a << b << c;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

7. cout/cin/endl などはすべて C++ 標準ライブラリに属します。 C++ 標準ライブラリは std (standard) という名前空間に配置されるため、

名前空間の使用を通じてそれらを使用します。

8、⼼日常の一般的な演習では名前空間 std を使用できますが、実際のプロジェクト開発で名前空間 std を使用することはお勧めできません。

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

1. デフォルトパラメータの定義と規定

デフォルト パラメーターは、関数を宣言または定義するときに、関数のパラメーターのデフォルト値を指定します。この関数を呼び出すときに、実際のパラメータが指定されていない場合

次に、仮パラメータのデフォルト値が使用され、それ以外の場合は、指定された実パラメータが使用されます。デフォルト パラメータは、完全なデフォルト パラメータと準デフォルト パラメータに分けられます。 (いくつかの場所で、

デフォルトパラメータはデフォルトパラメータとも呼ばれます)。

完全デフォルトは、すべての仮パラメータにデフォルト値が与えられることを意味し、半デフォルトは、一部の仮パラメータにデフォルト値が与えられることを意味します。C++ では、準デフォルトのパラメータは右から左に記述する必要があると規定しています。

Continuous は順番にデフォルト値に設定され、間隔を置いてデフォルト値にスキップすることはできません。

デフォルト パラメータを使用した関数呼び出しの場合、C++ では実パラメータを左から右に順番に指定する必要があり、実パラメータをスキップできないと規定しています。

関数の宣言と定義を分離する場合、デフォルトのパラメータを関数の宣言と定義の両方に含めることはできません。関数はデフォルトとして宣言する必要があると規定されています。

価値。

#include <iostream>
using namespace std;
void Func(int a = 0)
{
	cout << a << endl;
}
int main()
{
	Func(); // 没有传参时,使⽤参数的默认值
	Func(10); // 传参时,使⽤指定的实参
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

ここに画像の説明を挿入します

2. フルデフォルトとセミデフォルト

#include <iostream>
using namespace std;
// 全缺省
void Func1(int a = 10, int b = 20, int c = 30)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl << endl;
}
// 半缺省
void Func2(int a, int b = 10, int c = 20)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl << endl;
}
int main()
{
	Func1();
	Func1(1);
	Func1(1, 2);
	Func1(1, 2, 3);
	Func2(100);
	Func2(100, 200);
	Func2(100, 200, 300);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

セミデフォルトは次のように書くことはできません。

void Func2(int a = 10, int b, int c = 20)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl << endl;
}
//或者
void Func2(int a = 10, int b, int c)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl << endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

以下を厳守する必要があります。

準デフォルトのパラメータは右から左に継続的にデフォルト設定する必要があり、一定間隔でデフォルト値にスキップすることはできません。

3. デフォルトパラメータの実用化

C++ を学ぶ前、スタックの初期化と挿入を実装するときに、次のように書きました。

typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;
	int capacity;
}ST;
// 栈顶
void STInit(ST* ps, int n)
{
	assert(ps);
	ps->a = (STDataType*)malloc(n * sizeof(STDataType));
	ps->top = 0;
	ps->capacity = n;
}
void STPush(ST* ps, STDataType x)
{
	assert(ps);
	// 满了, 扩容
	if (ps->top == ps->capacity)
	{
		printf("扩容n");
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity
			* 2;
		STDataType* tmp = (STDataType*)realloc(ps->a,
			newcapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		ps->a = tmp;
		ps->capacity = newcapacity;
	}
	ps->a[ps->top] = x;
	ps->top++;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

100 個のデータを挿入する場合は、継続的な拡張と効率的な損失が必要になりますが、デフォルトのパラメーターを学習した後は、次のように記述できます。

// 栈顶
void STInit(ST* ps, int n = 4)
{
	assert(ps);
	ps->a = (STDataType*)malloc(n * sizeof(STDataType));
	ps->top = 0;
	ps->capacity = n;
}
void STPush(ST* ps, STDataType x)
{
	assert(ps);
	// 满了, 扩容
	if (ps->top == ps->capacity)
	{
		printf("扩容n");
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity
			* 2;
		STDataType* tmp = (STDataType*)realloc(ps->a,
			newcapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		ps->a = tmp;
		ps->capacity = newcapacity;
	}
	ps->a[ps->top] = x;
	ps->top++;
}
int main()
{
	ST a;
	STInit(&a, 100);//这里不传100也可以,因为规定必须从左到右依次给实参,不能跳跃给实参。刚好和缺省参数确定位置互补
	for (int i = 0; i < 100; i++)
	{
		STPush(&a, i);
	}
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

これにより、繰り返しスペースが空くという問題が効果的に回避されます。

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

C++ は、同じスコープ内に出現する同じ名前の関数をサポートしますが、同じ名前のこれらの関数の仮パラメータが異なる必要があります。これは、異なる数のパラメータであっても構いません。

他の種類。このように、C++ 関数呼び出しは多態性の動作を示し、より柔軟に使用できるようになります。 (ただし、異なる戻り値をオーバーロード条件として使用することはできません。

呼び出し時には区別できないため、戻り値とパラメータの型や数値が同時に変化する場合もオーバーロード状態となります)。

1. さまざまなパラメータの種類

#include<iostream>
using namespace std;
// 1、参数类型不同
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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2. パラメータの数が異なります

// 2、参数个数不同
void f()
{
	cout << "f()" << endl;
}
void f(int a)
{
	cout << "f(int a)" << endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

特に、上記のメソッドがデフォルトのパラメーターを使用している場合、パラメーターを渡さずに呼び出すと、コンパイラーは誰を呼び出すべきかを認識できません。

// 下⾯两个函数构成重载
// f()但是调⽤时,会报错,存在歧义,编译器不知道调⽤谁
void f1()
{
	cout << "f()" << endl;
}
void f1(int a = 10)
{
	cout << "f(int a)" << endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

3. パラメータの順序が違う(実際にはパラメータの型が違います)

// 3、参数类型顺序不同
void f(int a, char b)
{
	cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
	cout << "f(char b, int a)" << endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
#include<iostream>
using namespace std;
// 1、参数类型不同
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;
}
// 2、参数个数不同
void f()
{
	cout << "f()" << endl;
}
void f(int a)
{
	cout << "f(int a)" << endl;
}
void f(int a, char b)
{
	cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
	cout << "f(char b, int a)" << endl;
}
int main()
{
	Add(10, 20);
	Add(10.1, 20.2);
	f();
	f(10);
	f(10, 'a');
	f('a', 10);
	return 0;
}
`#include<iostream>
using namespace std;
// 1、参数类型不同
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;
}
// 2、参数个数不同
void f()
{
	cout << "f()" << endl;
}
void f(int a)
{
	cout << "f(int a)" << endl;
}
void f(int a, char b)
{
	cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
	cout << "f(char b, int a)" << endl;
}
int main()
{
	Add(10, 20);
	Add(10.1, 20.2);
	f();
	f(10);
	f(10, 'a');
	f('a', 10);
	return 0;
}``

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81

結果:
ここに画像の説明を挿入します