C++ はコードを使用してプログラミング学習ノートを改善します

 ダークホース プログラマー C++ 学習ノート01 テンプレート - テンプレート概念_哔哩哔哩_bilibili


 

目次

1 テンプレート

1.1 テンプレートの概念

1.2 関数テンプレート

1.2.1 関数テンプレートの構文

1.2.2 関数テンプレートの考慮事項

1.2.3 関数テンプレートの場合

1.2.4 通常関数と関数テンプレートの違い

1.2.5 通常関数および関数テンプレートの呼び出し規則

1.2.6 テンプレートの制限事項

1.3 クラステンプレート

1.3.1 クラステンプレートの構文

1.3.2 クラステンプレートと関数テンプレートの違い

1.3.3 クラス テンプレートでメンバー関数を作成する場合

1.3.4 関数パラメータとしてのクラステンプレートオブジェクト

1.3.5 クラステンプレートと継承

1.3.6 クラステンプレートメンバー関数のクラス外実装

1.3.7 クラステンプレートファイルのコンパイル

1.3.8 クラステンプレートと友達

1.3.9 クラステンプレートの例

2 STL の概要

2.1 STLの誕生

2.2 STLの基本概念

2.3 STL の 6 つのコンポーネント

2.4 STL のコンテナ、アルゴリズム、イテレータ

2.5 コンテナアルゴリズムイテレータの概要

2.5.1 ベクターストアの組み込みデータ型

2.5.2 Vector はカスタム データ型を保存します

2.5.3 ベクターコンテナのネストされたコンテナ

3 STL-共通コンテナ

3.1 文字列コンテナ

3.1.1 文字列の基本概念

3.1.2 文字列コンストラクター

3.1.3 文字列代入操作

3.1.4 文字列の連結

3.1.5 文字列の検索と置換

3.1.6 文字列の比較

3.1.7 文字列文字アクセス

3.1.8 文字列の挿入と削除

3.1.9 文字列の部分文字列

3.2 vector容器

3.2.1 ベクトルの基本概念

3.2.2 ベクトルコンストラクター

3.2.3 ベクトル代入演算

3.2.4 ベクトルの容量とサイズ

3.2.5 ベクトルの挿入と削除

3.2.6 ベクトルデータアクセス

3.2.7 ベクトルスワップコンテナ

3.2.8 ベクトル予約スペース

3.3 デックコンテナ

3.3.1 dequeコンテナの基本概念

3.3.2 両端キューコンストラクター

3.3.3 デキュー代入操作

3.3.4 デキューサイズの操作

3.3.5 deque の挿入と削除

3.3.6 データアクセスのデキュー

3.3.7 デックソート

3.4 事件 - 裁判官の採点

3.4.1 ケースの説明

3.4.2 実装手順

3.5スタックコンテナ

3.5.1 スタックの基本概念

3.5.2 スタック共通インターフェース

3.6 キューコンテナ

3.6.1 キューの基本概念

3.6.2 キュー共通インターフェース

3.7 リストコンテナ

3.7.1 リストの基本概念

3.7.2 リストコンストラクター

3.7.3 リストの割り当てと交換

3.7.4 リストサイズの操作

3.7.5 リストの挿入と削除

3.7.6 リストデータアクセス

3.7.7 リストの反転とソート

3.7.8 ケースのソート

3.8 set/ multiset 容器

3.8.1 集合の基本概念

3.8.2 セットの構築と代入

3.8.3 サイズとスワップの設定

3.8.4 セットの挿入と削除

3.8.5 セットの検索と統計

3.8.6 セットとマルチセットの違い

3.8.7 ペアの作成

3.8.8 コンテナのソートを設定する

3.9 マップ/マルチマップコンテナ

3.9.1 マップの基本概念

3.9.2 マップの構築と割り当て

3.9.3 マップのサイズとスワップ

3.9.4 マップの挿入と削除

3.9.5 地図の検索と統計

3.9.6 マップコンテナのソート

3.10 ケース - 従業員のグループ化

3.10.1 ケースの説明

3.10.2 実装手順

4 STL 関数オブジェクト

4.1 関数オブジェクト

4.1.1 関数オブジェクトの概念

4.1.2 関数オブジェクトの使用法

4.2 述語

4.2.1 述語の概念

4.2.2 単項述語

4.2.3 バイナリ述語

4.3 組み込み関数オブジェクト

4.3.1 組み込み関数オブジェクトの意味

4.3.2 算術関手

4.3.3 関係関数

4.3.4 論理ファンクター

5 つの STL 共通アルゴリズム

5.1 一般的に使用されるトラバーサル アルゴリズム

5.1.1 for_each

5.1.2 変換

5.2 一般的な検索アルゴリズム

5.2.1 検索

5.2.2 find_if

5.2.3 隣接検索

5.2.4 バイナリ検索

5.2.5カウント

5.2.6 count_if

5.3 一般的な並べ替えアルゴリズム

5.3.1 並べ替え

5.3.2 ランダムシャッフル

5.3.3 マージ

5.3.4 リバース

5.4 一般的なコピーおよび置換アルゴリズム

5.4.1 コピー

5.4.2 置換

5.4.3 replace_if

5.4.4 スワップ

5.5 一般的な算術生成アルゴリズム

5.5.1 蓄積

5.5.2 塗りつぶし

5.6 共通の集合アルゴリズム

5.6.1 set_intersection

5.6.2 set_union

5.6.3 set_difference


このステージでは主に C++ の汎用プログラミングSTLテクノロジ の詳細な説明に焦点を当て、C++ のより深い使用方法を探ります。

1 テンプレート

1.1 テンプレートの概念

テンプレートは一般的な金型を作成するもので、再利用性が大幅に向上します

テンプレートの特徴:

  • テンプレートは直接使用することはできません。これは単なるフレームワークです

  • テンプレートの汎用性は万能薬ではありません

1.2 関数テンプレート

  • C++ のもう 1 つのプログラミングのアイデアはジェネリック、使用される主なテクノロジはテンプレートです。

  • C++ は、関数テンプレートクラス テンプレートという2 つのテンプレート メカニズムを提供します。

 

1.2.1 関数テンプレートの構文

関数テンプレートの役割:

一般的な関数を作成する場合、その関数の戻り値の型と仮パラメータの型を指定することはできませんが、仮想型で表現することができます。

 文法:

template<typename T>
函数声明或定义

 

説明:

template --- 作成テンプレートを宣言します

typename --- 表面上でその後ろにあるシンボルはデータ型であり、クラスで置き換えることができます。

T --- 一般的なデータ型、名前は置換可能、通常は大文字

 

 例:


//交换整型函数
void swapInt(int& a, int& b) {
	int temp = a;
	a = b;
	b = temp;
}

//交换浮点型函数
void swapDouble(double& a, double& b) {
	double temp = a;
	a = b;
	b = temp;
}

//利用模板提供通用的交换函数
template<typename T>
void mySwap(T& a, T& b)
{
	T temp = a;
	a = b;
	b = temp;
}

void test01()
{
	int a = 10;
	int b = 20;
	
	//swapInt(a, b);

	//利用模板实现交换
	//1、自动类型推导
	mySwap(a, b);

	//2、显示指定类型
	mySwap<int>(a, b);

	cout << "a = " << a << endl;
	cout << "b = " << b << endl;

}

int main() {

	test01();

	system("pause");

	return 0;
}

要約:

  • 関数テンプレートはキーワード テンプレートを使用します

  • 関数テンプレートを使用するには、自動型推論と明示的に型を指定する 2 つの方法があります。

  • テンプレートの目的は、再利用性を向上させ、型をパラメータ化することです。

1.2.2 関数テンプレートの考慮事項

予防:

  • 自動型推定。使用する前に一貫したデータ型 T を推定する必要があります。

  • テンプレートは、使用する前に T のデータ型を決定する必要があります。

 例:

//利用模板提供通用的交换函数
template<class T>//typename可以替换成class
void mySwap(T& a, T& b)
{
	T temp = a;
	a = b;
	b = temp;
}


// 1、自动类型推导,必须推导出一致的数据类型T,才可以使用
void test01()
{
	int a = 10;
	int b = 20;
	char c = 'c';

	mySwap(a, b); // 正确,可以推导出一致的T
	//mySwap(a, c); // 错误,推导不出一致的T类型
}


// 2、模板必须要确定出T的数据类型,才可以使用
template<class T>
void func()
{
	cout << "func 调用" << endl;
}

void test02()
{
	//func(); //错误,模板不能独立使用,必须确定出T的类型
	func<int>(); //利用显示指定类型的方式,给T一个类型,才可以使用该模板
}

int main() {

	test01();
	test02();

	system("pause");

	return 0;
}

要約:

  • テンプレートを使用する場合、一般的なデータ型 T を決定する必要があり、一貫した型を推定できます。

 

1.2.3 関数テンプレートの場合

ケースの説明:

  • 関数テンプレートを使用して、さまざまなデータ型の配列を並べ替えることができる並べ替え関数をカプセル化します。

  • ソートルールは大から小まであり、ソートアルゴリズムは選択ソートです。

  • char 配列int 配列をそれぞれ使用してテストする

 例:

//交换的函数模板
template<typename T>
void mySwap(T &a, T&b)
{
	T temp = a;
	a = b;
	b = temp;
}


template<class T> // 也可以替换成typename
//利用选择排序,进行对数组从大到小的排序
void mySort(T arr[], int len)
{
	for (int i = 0; i < len; i++)
	{
		int max = i; //最大数的下标
		for (int j = i + 1; j < len; j++)
		{
			if (arr[max] < arr[j])
			{
				max = j;
			}
		}
		if (max != i) //如果最大数的下标不是i,交换两者
		{
			mySwap(arr[max], arr[i]);
		}
	}
}
template<typename T>
void printArray(T arr[], int len) {

	for (int i = 0; i < len; i++) {
		cout << arr[i] << " ";
	}
	cout << endl;
}
void test01()
{
	//测试char数组
	char charArr[] = "bdcfeagh";
	int num = sizeof(charArr) / sizeof(char);
	mySort(charArr, num);
	printArray(charArr, num);
}

void test02()
{
	//测试int数组
	int intArr[] = { 7, 5, 8, 1, 3, 9, 2, 4, 6 };
	int num = sizeof(intArr) / sizeof(int);
	mySort(intArr, num);
	printArray(intArr, num);
}

int main() {

	test01();
	test02();

	system("pause");

	return 0;
}

 概要: テンプレートはコードの再利用を向上させることができるため、使いこなす必要があります

1.2.4 通常関数と関数テンプレートの違い

通常の関数と関数テンプレートの違いは次のとおりです。

  • 通常の関数呼び出し時に自動型変換 (暗黙的な型変換) が発生する可能性があります

  • 関数テンプレートが呼び出されたときに、自動型推論が使用されている場合、暗黙的な型変換は発生しません。

  • 明示的な型が指定されている場合、暗黙的な型変換が発生する可能性があります

例:

//普通函数
int myAdd01(int a, int b)
{
	return a + b;
}

//函数模板
template<class T>
T myAdd02(T a, T b)  
{
	return a + b;
}

//使用函数模板时,如果用自动类型推导,不会发生自动类型转换,即隐式类型转换
void test01()
{
	int a = 10;
	int b = 20;
	char c = 'c';
	
	cout << myAdd01(a, c) << endl; //正确,将char类型的'c'隐式转换为int类型  'c' 对应 ASCII码 99

	//myAdd02(a, c); // 报错,使用自动类型推导时,不会发生隐式类型转换

	myAdd02<int>(a, c); //正确,如果用显示指定类型,可以发生隐式类型转换
}

int main() {

	test01();

	system("pause");

	return 0;
}

まとめ:大まかな型Tは自分で決めることができるため、指定した型を表示して関数テンプレートを呼び出す方法を使うことをお勧めします

 

1.2.5 通常関数および関数テンプレートの呼び出し規則

呼び出しルールは次のとおりです。

  1. 関数テンプレートと通常の関数の両方を実装できる場合は、通常の関数が最初に呼び出されます。

  2. 空のテンプレートパラメータリストを使用して関数テンプレートを強制的に呼び出すことができます

  3. 関数テンプレートはオーバーロードすることもできます

  4. 関数テンプレートの方がより適切な一致を生成できる場合、関数テンプレートが最初に呼び出されます。

例:

//普通函数与函数模板调用规则
void myPrint(int a, int b)
{
	cout << "调用的普通函数" << endl;
}

template<typename T>
void myPrint(T a, T b) 
{ 
	cout << "调用的模板" << endl;
}

template<typename T>
void myPrint(T a, T b, T c) 
{ 
	cout << "调用重载的模板" << endl; 
}

void test01()
{
	//1、如果函数模板和普通函数都可以实现,优先调用普通函数
	// 注意 如果告诉编译器  普通函数是有的,但只是声明没有实现,或者不在当前文件内实现,就会报错找不到
	int a = 10;
	int b = 20;
	myPrint(a, b); //调用普通函数

	//2、可以通过空模板参数列表来强制调用函数模板
	myPrint<>(a, b); //调用函数模板

	//3、函数模板也可以发生重载
	int c = 30;
	myPrint(a, b, c); //调用重载的函数模板

	//4、 如果函数模板可以产生更好的匹配,优先调用函数模板
	char c1 = 'a';
	char c2 = 'b';
	myPrint(c1, c2); //调用函数模板
}

int main() {

	test01();

	system("pause");

	return 0;
}

まとめ: 関数テンプレートが提供されているため、通常の関数は提供しないほうが良いです。そうしないと曖昧さが生じやすくなります。

 

1.2.6 テンプレートの制限事項

 

制限:

  • テンプレートの汎用性がすべてではない

 例えば:

	template<class T>
	void f(T a, T b)
	{ 
    	a = b;
    }

上記のコードで提供されている代入演算は、渡された a と b が配列の場合には実現できません。

もう一つの例:  

	template<class T>
	void f(T a, T b)
	{ 
    	if(a > b) { ... }
    }

上記のコードでは、T のデータ型が Person などのカスタム データ型で渡されると、正しく動作しません。

したがって、この問題を解決するために、C++ は、これらの特定の型に特定のテンプレートを提供できるテンプレート オーバーロードを提供します

 

例:  

#include<iostream>
using namespace std;

#include <string>

class Person
{
public:
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	string m_Name;
	int m_Age;
};

//普通函数模板
template<class T>
bool myCompare(T& a, T& b)
{
	if (a == b)
	{
		return true;
	}
	else
	{
		return false;
	}
}


//具体化,显示具体化的原型和定意思以template<>开头,并通过名称来指出类型
//具体化优先于常规模板
template<> bool myCompare(Person &p1, Person &p2)
{
	if ( p1.m_Name  == p2.m_Name && p1.m_Age == p2.m_Age)
	{
		return true;
	}
	else
	{
		return false;
	}
}

void test01()
{
	int a = 10;
	int b = 20;
	//内置数据类型可以直接使用通用的函数模板
	bool ret = myCompare(a, b);
	if (ret)
	{
		cout << "a == b " << endl;
	}
	else
	{
		cout << "a != b " << endl;
	}
}

void test02()
{
	Person p1("Tom", 10);
	Person p2("Tom", 10);
	//自定义数据类型,不会调用普通的函数模板
	//可以创建具体化的Person数据类型的模板,用于特殊处理这个类型
	bool ret = myCompare(p1, p2);
	if (ret)
	{
		cout << "p1 == p2 " << endl;
	}
	else
	{
		cout << "p1 != p2 " << endl;
	}
}

int main() {

	test01();

	test02();

	system("pause");

	return 0;
}

要約:

  • カスタムタイプの一般化は、具体的なテンプレートを使用することで解決できます

  • テンプレートの学習とは、テンプレートを書くことではなく、システムが提供するテンプレートを STL で使用することです。

 

1.3 クラステンプレート

 

1.3.1 クラステンプレートの構文

クラステンプレートの役割:

  • 一般的なクラスを作成すると、クラス内のメンバーのデータ型は、を指定せずに仮想型で表現できます。

 文法:

template<typename T>
类

説明:

template --- 作成テンプレートを宣言します

typename --- 表面上でその後ろにあるシンボルはデータ型であり、クラスで置き換えることができます。

T --- 一般的なデータ型、名前は置換可能、通常は大文字

 

例:  

#include <string>
//类模板
template<class NameType, class AgeType> 
class Person
{
public:
	Person(NameType name, AgeType age)
	{
		this->mName = name;
		this->mAge = age;
	}
	void showPerson()
	{
		cout << "name: " << this->mName << " age: " << this->mAge << endl;
	}
public:
	NameType mName;
	AgeType mAge;
};

void test01()
{
	// 指定NameType 为string类型,AgeType 为 int类型
	Person<string, int>P1("孙悟空", 999);
	P1.showPerson();
}

int main() {

	test01();

	system("pause");

	return 0;
}

概要: クラス テンプレートと関数テンプレートの構文は似ており、宣言テンプレート テンプレートの後にクラスを追加します。これはクラス テンプレートと呼ばれます。

1.3.2 クラステンプレートと関数テンプレートの違い

クラス テンプレートと関数テンプレートの間には、主に 2 つの違いがあります。

  1. 自動型推定を行わないクラス テンプレートの使用

  2. クラス テンプレートには、テンプレート パラメータ リストにデフォルト パラメータを含めることができます

 

 例:

#include <string>
//类模板
template<class NameType, class AgeType = int> 
class Person
{
public:
	Person(NameType name, AgeType age)
	{
		this->mName = name;
		this->mAge = age;
	}
	void showPerson()
	{
		cout << "name: " << this->mName << " age: " << this->mAge << endl;
	}
public:
	NameType mName;
	AgeType mAge;
};

//1、类模板没有自动类型推导的使用方式
void test01()
{
	// Person p("孙悟空", 1000); // 错误 类模板使用时候,不可以用自动类型推导
	Person <string ,int>p("孙悟空", 1000); //必须使用显示指定类型的方式,使用类模板
	p.showPerson();
}

//2、类模板在模板参数列表中可以有默认参数
void test02()
{
	Person <string> p("猪八戒", 999); //类模板中的模板参数列表 可以指定默认参数
	p.showPerson();
}

int main() {

	test01();

	test02();

	system("pause");

	return 0;
}

要約:

  • クラス テンプレートは、タイプを明示的に指定する場合にのみ使用できます。

  • クラス テンプレートのテンプレート パラメーター リストにはデフォルト パラメーターを含めることができます

 

1.3.3 クラス テンプレートでメンバー関数を作成する場合

クラス テンプレートのメンバー関数と通常のクラスのメンバー関数の作成タイミングには違いがあります。

  • 通常クラスのメンバー関数は最初から作成可能

  • クラス テンプレート内のメンバー関数は、呼び出されたときに作成されます。

 例:

class Person1
{
public:
	void showPerson1()
	{
		cout << "Person1 show" << endl;
	}
};

class Person2
{
public:
	void showPerson2()
	{
		cout << "Person2 show" << endl;
	}
};

template<class T>
class MyClass
{
public:
	T obj;

	//类模板中的成员函数,并不是一开始就创建的,而是在模板调用时再生成

	void fun1() { obj.showPerson1(); }
	void fun2() { obj.showPerson2(); }

};

void test01()
{
	MyClass<Person1> m;
	
	m.fun1();

	//m.fun2();//编译会出错,说明函数调用才会去创建成员函数
}

int main() {

	test01();

	system("pause");

	return 0;
}

概要: クラス テンプレートのメンバー関数は最初には作成されず、呼び出されたときにのみ作成されます。

1.3.4 関数パラメータとしてのクラステンプレートオブジェクト

 

学習目標:

  • クラステンプレートからインスタンス化されたオブジェクト、関数にパラメータを渡す方法

入力方法は 3 つあります。

  1. 受信タイプを指定 --- オブジェクトのデータタイプを直接表示します

  2. パラメータのテンプレート化 --- オブジェクト内のパラメータを渡すためのテンプレートに変換します。

  3. クラス全体がテンプレート化されます --- 渡すオブジェクト型をテンプレート化します

例:  

#include <string>
//类模板
template<class NameType, class AgeType = int> 
class Person
{
public:
	Person(NameType name, AgeType age)
	{
		this->mName = name;
		this->mAge = age;
	}
	void showPerson()
	{
		cout << "name: " << this->mName << " age: " << this->mAge << endl;
	}
public:
	NameType mName;
	AgeType mAge;
};

//1、指定传入的类型
void printPerson1(Person<string, int> &p) 
{
	p.showPerson();
}
void test01()
{
	Person <string, int >p("孙悟空", 100);
	printPerson1(p);
}

//2、参数模板化
template <class T1, class T2>
void printPerson2(Person<T1, T2>&p)
{
	p.showPerson();
	cout << "T1的类型为: " << typeid(T1).name() << endl;
	cout << "T2的类型为: " << typeid(T2).name() << endl;
}
void test02()
{
	Person <string, int >p("猪八戒", 90);
	printPerson2(p);
}

//3、整个类模板化
template<class T>
void printPerson3(T & p)
{
	cout << "T的类型为: " << typeid(T).name() << endl;
	p.showPerson();

}
void test03()
{
	Person <string, int >p("唐僧", 30);
	printPerson3(p);
}

int main() {

	test01();
	test02();
	test03();

	system("pause");

	return 0;
}

要約:

  • クラス テンプレートを通じて作成されたオブジェクトは、3 つの方法で関数にパラメータを渡すことができます

  • より広く使用されているのは最初の方法です。受信タイプを指定します。

 

1.3.5 クラステンプレートと継承

クラス テンプレートで継承が発生した場合は、次の点に注意する必要があります。

  • サブクラスが継承する親クラスがクラステンプレートの場合、サブクラスは宣言時に親クラスのTの型を指定する必要があります。

  • 指定しない場合、コンパイラはサブクラスにメモリを割り当てることができません。

  • 親クラスでTの型を柔軟に指定したい場合は、サブクラスもクラステンプレートにする必要があります

 

 例:

template<class T>
class Base
{
	T m;
};

//class Son:public Base  //错误,c++编译需要给子类分配内存,必须知道父类中T的类型才可以向下继承
class Son :public Base<int> //必须指定一个类型
{
};
void test01()
{
	Son c;
}

//类模板继承类模板 ,可以用T2指定父类中的T类型
template<class T1, class T2>
class Son2 :public Base<T2>
{
public:
	Son2()
	{
		cout << typeid(T1).name() << endl;
		cout << typeid(T2).name() << endl;
	}
};

void test02()
{
	Son2<int, char> child1;
}


int main() {

	test01();

	test02();

	system("pause");

	return 0;
}

概要: 親クラスがクラス テンプレートの場合、サブクラスは親クラスの T のデータ型を指定する必要があります。  

 

1.3.6 クラステンプレートメンバー関数のクラス外実装

学習目標: クラス外のクラス テンプレートでのメンバー関数の実装をマスターできるようになること

 例:

#include <string>

//类模板中成员函数类外实现
template<class T1, class T2>
class Person {
public:
	//成员函数类内声明
	Person(T1 name, T2 age);
	void showPerson();

public:
	T1 m_Name;
	T2 m_Age;
};

//构造函数 类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
	this->m_Name = name;
	this->m_Age = age;
}

//成员函数 类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson() {
	cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
}

void test01()
{
	Person<string, int> p("Tom", 20);
	p.showPerson();
}

int main() {

	test01();

	system("pause");

	return 0;
}

概要: クラス テンプレートのメンバー関数がクラスの外部で実装される場合、テンプレート パラメーター リストを追加する必要があります。

1.3.7 クラステンプレートファイルのコンパイル

学習目標:

  • ファイルをクラステンプレートのメンバー関数に分割することで生じる問題と解決策をマスターする

質問:

  • クラス テンプレートのメンバー関数の作成時が呼び出しフェーズにあるため、サブファイルの書き込み時にリンクに失敗します。

解決:

  • 解決策 1: .cpp ソース ファイルを直接インクルードする

  • 解決策 2: 宣言と実装を同じファイルに書き込み、サフィックスを .hpp に変更します。hpp は合意された名前であり、必須ではありません。

例:

person.hpp のコード:

#pragma once
#include <iostream>
using namespace std;
#include <string>

template<class T1, class T2>
class Person {
public:
	Person(T1 name, T2 age);
	void showPerson();
public:
	T1 m_Name;
	T2 m_Age;
};

//构造函数 类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
	this->m_Name = name;
	this->m_Age = age;
}

//成员函数 类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson() {
	cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
}

クラス テンプレートをファイルに分割して .cpp にコードを記述します。

#include<iostream>
using namespace std;

//#include "person.h"
#include "person.cpp" //解决方式1,包含cpp源文件

//解决方式2,将声明和实现写到一起,文件后缀名改为.hpp
#include "person.hpp"
void test01()
{
	Person<string, int> p("Tom", 10);
	p.showPerson();
}

int main() {

	test01();

	system("pause");

	return 0;
}

概要: 主流の解決策は 2 番目の解決策です。クラス テンプレートのメンバー関数を一緒に記述し、サフィックス名を .hpp に変更します。

 

1.3.8 クラステンプレートと友達

学習目標:

  • クラステンプレートとフレンド関数のクラス内およびクラス外の実装をマスターする

クラス内でのグローバル関数の実装 - クラス内で直接フレンドを宣言するだけです

グローバル関数のクラス外実装 - グローバル関数の存在を事前にコンパイラに知らせる必要がある

 例:

#include <string>

//2、全局函数配合友元  类外实现 - 先做函数模板声明,下方在做函数模板定义,在做友元
template<class T1, class T2> class Person;

//如果声明了函数模板,可以将实现写到后面,否则需要将实现体写到类的前面让编译器提前看到
//template<class T1, class T2> void printPerson2(Person<T1, T2> & p); 

template<class T1, class T2>
void printPerson2(Person<T1, T2> & p)
{
	cout << "类外实现 ---- 姓名: " << p.m_Name << " 年龄:" << p.m_Age << endl;
}

template<class T1, class T2>
class Person
{
	//1、全局函数配合友元   类内实现
	friend void printPerson(Person<T1, T2> & p)
	{
		cout << "姓名: " << p.m_Name << " 年龄:" << p.m_Age << endl;
	}


	//全局函数配合友元  类外实现
	friend void printPerson2<>(Person<T1, T2> & p);

public:

	Person(T1 name, T2 age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}


private:
	T1 m_Name;
	T2 m_Age;

};

//1、全局函数在类内实现
void test01()
{
	Person <string, int >p("Tom", 20);
	printPerson(p);
}


//2、全局函数在类外实现
void test02()
{
	Person <string, int >p("Jerry", 30);
	printPerson2(p);
}

int main() {

	//test01();

	test02();

	system("pause");

	return 0;
}

概要: グローバル関数はクラスに実装することをお勧めします。使用法が簡単で、コンパイラが直接認識できるためです。

 

1.3.9 クラステンプレートの例

ケースの説明: 一般的な配列クラスを実装するには、次の要件があります。

  • 組み込みデータ型とカスタムデータ型のデータを保存可能

  • 配列内のデータをヒープに保存します

  • コンストラクターに渡すことができる配列の容量

  • 浅いコピーの問題を防ぐために、対応するコピー コンストラクターと演算子=を提供します。

  • 配列内のデータを追加および削除するための末尾挿入および末尾削除メソッドを提供します。

  • 配列内の要素には添字を付けることでアクセスできます。

  • 配列内の現在の要素数と配列の容量を取得できます。

 例:

myArray.hpp のコード  

#pragma once
#include <iostream>
using namespace std;

template<class T>
class MyArray
{
public:
    
	//构造函数
	MyArray(int capacity)
	{
		this->m_Capacity = capacity;
		this->m_Size = 0;
		pAddress = new T[this->m_Capacity];
	}

	//拷贝构造
	MyArray(const MyArray & arr)
	{
		this->m_Capacity = arr.m_Capacity;
		this->m_Size = arr.m_Size;
		this->pAddress = new T[this->m_Capacity];//深拷贝
		for (int i = 0; i < this->m_Size; i++)
		{
			//如果T为对象,而且还包含指针,必须需要重载 = 操作符,因为这个等号不是 构造 而是赋值,
			// 普通类型可以直接= 但是指针类型需要深拷贝
			this->pAddress[i] = arr.pAddress[i];
		}
	}

	//重载= 操作符  防止浅拷贝问题
	MyArray& operator=(const MyArray& myarray) {

		if (this->pAddress != NULL) {
			delete[] this->pAddress;
			this->m_Capacity = 0;
			this->m_Size = 0;
		}

		this->m_Capacity = myarray.m_Capacity;
		this->m_Size = myarray.m_Size;
		this->pAddress = new T[this->m_Capacity];
		for (int i = 0; i < this->m_Size; i++) {
			this->pAddress[i] = myarray[i];
		}
		return *this;
	}

	//重载[] 操作符  arr[0]
	T& operator [](int index)
	{
		return this->pAddress[index]; //不考虑越界,用户自己去处理
	}

	//尾插法
	void Push_back(const T & val)
	{
		if (this->m_Capacity == this->m_Size)
		{
			return;
		}
		this->pAddress[this->m_Size] = val;
		this->m_Size++;
	}

	//尾删法
	void Pop_back()
	{
		if (this->m_Size == 0)
		{
			return;
		}
		this->m_Size--;
	}

	//获取数组容量
	int getCapacity()
	{
		return this->m_Capacity;
	}

	//获取数组大小
	int	getSize()
	{
		return this->m_Size;
	}


	//析构
	~MyArray()
	{
		if (this->pAddress != NULL)
		{
			delete[] this->pAddress;
			this->pAddress = NULL;
			this->m_Capacity = 0;
			this->m_Size = 0;
		}
	}

private:
	T * pAddress;  //指向一个堆空间,这个空间存储真正的数据
	int m_Capacity; //容量
	int m_Size;   // 大小
};

 クラス テンプレートのケース - 配列クラス encapsulation.cpp

#include "myArray.hpp"
#include <string>

void printIntArray(MyArray<int>& arr) {
	for (int i = 0; i < arr.getSize(); i++) {
		cout << arr[i] << " ";
	}
	cout << endl;
}

//测试内置数据类型
void test01()
{
	MyArray<int> array1(10);
	for (int i = 0; i < 10; i++)
	{
		array1.Push_back(i);
	}
	cout << "array1打印输出:" << endl;
	printIntArray(array1);
	cout << "array1的大小:" << array1.getSize() << endl;
	cout << "array1的容量:" << array1.getCapacity() << endl;

	cout << "--------------------------" << endl;

	MyArray<int> array2(array1);
	array2.Pop_back();
	cout << "array2打印输出:" << endl;
	printIntArray(array2);
	cout << "array2的大小:" << array2.getSize() << endl;
	cout << "array2的容量:" << array2.getCapacity() << endl;
}

//测试自定义数据类型
class Person {
public:
	Person() {} 
		Person(string name, int age) {
		this->m_Name = name;
		this->m_Age = age;
	}
public:
	string m_Name;
	int m_Age;
};

void printPersonArray(MyArray<Person>& personArr)
{
	for (int i = 0; i < personArr.getSize(); i++) {
		cout << "姓名:" << personArr[i].m_Name << " 年龄: " << personArr[i].m_Age << endl;
	}

}

void test02()
{
	//创建数组
	MyArray<Person> pArray(10);
	Person p1("孙悟空", 30);
	Person p2("韩信", 20);
	Person p3("妲己", 18);
	Person p4("王昭君", 15);
	Person p5("赵云", 24);

	//插入数据
	pArray.Push_back(p1);
	pArray.Push_back(p2);
	pArray.Push_back(p3);
	pArray.Push_back(p4);
	pArray.Push_back(p5);

	printPersonArray(pArray);

	cout << "pArray的大小:" << pArray.getSize() << endl;
	cout << "pArray的容量:" << pArray.getCapacity() << endl;

}

int main() {

	//test01();

	test02();

	system("pause");

	return 0;
}

要約:

学習した知識ポイントを利用して汎用配列を実現できる

 

2 STL の概要

2.1 STLの誕生

  • ソフトウェアの世界は長い間、再利用可能なものを構築したいと考えてきました。

  • C++ のオブジェクト指向および汎用プログラミングのアイデアは、再利用性の向上を目的としています。

  • ほとんどの場合、データ構造とアルゴリズムの標準がないため、多くの反復作業が発生します。

  • データ構造とアルゴリズムの一連の標準を確立するために、 STL が誕生しました。

 

2.2 STLの基本概念

  • STL(標準テンプレートライブラリ、標準テンプレートライブラリ

  • STLは大きく分けて、コンテナ(container) アルゴリズム(algorithm) イテレータ(iterator)

  • コンテナアルゴリズムの間には、反復子を介したシームレスな接続が存在します

  • STL のほとんどすべてのコードは、テンプレート クラスまたはテンプレート関数を使用します。

2.3 STL の 6 つのコンポーネント

STLはコンテナ、アルゴリズム、イテレータ、ファンクタ、アダプタ(アダプタ)、空間コンフィギュレータの6つの主要なコンポーネントに大別されます。

  1. コンテナ: データの保存には、ベクター、リスト、デキュー、セット、マップなどのさまざまなデータ構造が使用されます。

  2. アルゴリズム: ソート、検索、コピー、for_each などの一般的に使用されるさまざまなアルゴリズム。

  3. イテレータ: コンテナとアルゴリズムの間の接着剤として機能します。

  4. ファンクター: 関数のように動作し、アルゴリズムのある種の戦略として使用できます。

  5. アダプター: コンテナー、ファンクター、またはイテレーターのインターフェースを修飾するために使用されるもの。

  6. スペースコンフィギュレーター: スペースの構成と管理を担当します。

 

2.4 STL のコンテナ、アルゴリズム、イテレータ

コンテナ:保管場所

STLコンテナは最も広く使用されているデータ構造のいくつかを実現します。

一般的に使用されるデータ構造: 配列、リンク リスト、ツリー、スタック、キュー、コレクション、マッピング テーブルなど。

これらのコンテナは、順次コンテナ連想コンテナの2 つのタイプに分類されます

シーケンシャル コンテナ: 値の並べ替えを強調し、シーケンシャル コンテナ内の各要素の位置は固定されています。

連想コンテナ: バイナリ ツリー構造。要素間に厳密な物理的順序関係はありません。

アルゴリズム:問題の解決策

論理的または数学的問題を解決するための限られた手順。この主題をアルゴリズム (アルゴリズム) と呼びます。

アルゴリズムは、質的変化アルゴリズム非質的変化アルゴリズムに分けられます。

質的変更アルゴリズム: 操作中に区間内の要素の内容が変更されることを意味します。コピー、置換、削除など。

非定性アルゴリズム: 区間内の要素の内容が、検索、カウント、トラバース、極値の検索などの操作中に変更されないことを意味します。

イテレータ:コンテナとアルゴリズムの間の接着剤

コンテナの内部表現を公開せずに、コンテナに含まれる要素への順次アクセスを可能にするメソッドを提供します。

各コンテナには独自のイテレータがあります

イテレータの使用法はポインタと非常に似ており、最初の段階では、まずイテレータがポインタであることを理解できます。

イテレータの型:

タイプ 関数 サポート業務
入力反復子 データへの読み取り専用アクセス 読み取り専用。++、==、! をサポートします。=
出力反復子 データへの書き込み専用アクセス 書き込みのみ、++ をサポート
前方反復子 読み取りおよび書き込み操作を実行し、イテレータを進めることができます 読み取りと書き込み、++、== をサポート! =
双方向反復子 読み取りおよび書き込み操作、および順方向および逆方向の操作が可能 読み取りと書き込み、++ のサポート、--、
ランダムアクセス反復子 読み取りおよび書き込み操作、ジャンプ方式で任意のデータにアクセスできる、最も強力なイテレータ 読み取りと書き込み、++、--、[n]、-n、<、<=、>、>= をサポート

コンテナ内で一般的に使用されるイテレータのタイプは、双方向イテレータとランダム アクセス イテレータです。  

2.5 コンテナアルゴリズムイテレータの概要

STLのコンテナ、アルゴリズム、イテレータの概念を理解した上で、コードを使ってSTLの魅力を感じてみましょう

STL で最も一般的に使用されるコンテナーは配列として理解できる Vector です。次に、このコンテナーにデータを挿入し、このコンテナーを走査する方法を学びます。

2.5.1 ベクターストアの組み込みデータ型

容器:vector

アルゴリズム:for_each

反復子:vector<int>::iterator

 例:

#include <vector>
#include <algorithm>

void MyPrint(int val)
{
	cout << val << endl;
}

void test01() {

	//创建vector容器对象,并且通过模板参数指定容器中存放的数据的类型
	vector<int> v;
	//向容器中放数据
	v.push_back(10);
	v.push_back(20);
	v.push_back(30);
	v.push_back(40);

	//每一个容器都有自己的迭代器,迭代器是用来遍历容器中的元素
	//v.begin()返回迭代器,这个迭代器指向容器中第一个数据
	//v.end()返回迭代器,这个迭代器指向容器元素的最后一个元素的下一个位置
	//vector<int>::iterator 拿到vector<int>这种容器的迭代器类型

	vector<int>::iterator pBegin = v.begin();
	vector<int>::iterator pEnd = v.end();

	//第一种遍历方式:
	while (pBegin != pEnd) {
		cout << *pBegin << endl;
		pBegin++;
	}

	
	//第二种遍历方式:
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
		cout << *it << endl;
	}
	cout << endl;

	//第三种遍历方式:
	//使用STL提供标准遍历算法  头文件 algorithm
	for_each(v.begin(), v.end(), MyPrint);
}

int main() {

	test01();

	system("pause");

	return 0;
}

2.5.2 Vector はカスタム データ型を保存します

学習目標: カスタム データ型をベクターに保存して出力する

例:  

#include <vector>
#include <string>

//自定义数据类型
class Person {
public:
	Person(string name, int age) {
		mName = name;
		mAge = age;
	}
public:
	string mName;
	int mAge;
};
//存放对象
void test01() {

	vector<Person> v;

	//创建数据
	Person p1("aaa", 10);
	Person p2("bbb", 20);
	Person p3("ccc", 30);
	Person p4("ddd", 40);
	Person p5("eee", 50);

	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	v.push_back(p5);

	for (vector<Person>::iterator it = v.begin(); it != v.end(); it++) {
		cout << "Name:" << (*it).mName << " Age:" << (*it).mAge << endl;

	}
}


//放对象指针
void test02() {

	vector<Person*> v;

	//创建数据
	Person p1("aaa", 10);
	Person p2("bbb", 20);
	Person p3("ccc", 30);
	Person p4("ddd", 40);
	Person p5("eee", 50);

	v.push_back(&p1);
	v.push_back(&p2);
	v.push_back(&p3);
	v.push_back(&p4);
	v.push_back(&p5);

	for (vector<Person*>::iterator it = v.begin(); it != v.end(); it++) {
		Person * p = (*it);
		cout << "Name:" << p->mName << " Age:" << (*it)->mAge << endl;
	}
}


int main() {

	test01();
    
	test02();

	system("pause");

	return 0;
}

2.5.3 ベクターコンテナのネストされたコンテナ

学習目標: コンテナはコンテナ内にネストされており、すべてのデータを走査して出力します。

例:  

#include <vector>

//容器嵌套容器
void test01() {

	vector< vector<int> >  v;

	vector<int> v1;
	vector<int> v2;
	vector<int> v3;
	vector<int> v4;

	for (int i = 0; i < 4; i++) {
		v1.push_back(i + 1);
		v2.push_back(i + 2);
		v3.push_back(i + 3);
		v4.push_back(i + 4);
	}

	//将容器元素插入到vector v中
	v.push_back(v1);
	v.push_back(v2);
	v.push_back(v3);
	v.push_back(v4);


	for (vector<vector<int>>::iterator it = v.begin(); it != v.end(); it++) 
    {
        //(*it)===容器 vector<int>
		for (vector<int>::iterator vit = (*it).begin(); vit != (*it).end(); vit++) {
			cout << *vit << " ";
		}
		cout << endl;
	}

}

int main() {

	test01();

	system("pause");

	return 0;
}

3 STL-共通コンテナ

3.1 文字列コンテナ

3.1.1 文字列の基本概念

自然:

  • string は C++ スタイルの文字列であり、string は本質的にクラスです。

文字列と文字 * の違い:

  • char * はポインタです

  • string は char* をクラス内にカプセル化し、char* 型のコンテナであるこの文字列を管理するクラスです。

特徴:

文字列クラスは多くのメンバー メソッドを内部的にカプセル化します。

例: find 検索、コピー コピー、削除 削除 置換 置換、挿入 挿入

String は char* によって割り当てられたメモリを管理するため、範囲外のコピーや範囲外の値などを心配する必要はなく、クラスがそれを担当します。

3.1.2 文字列コンストラクター

コンストラクターのプロトタイプ:

  • string();//次のような空の文字列を作成します。

  • string(const char* s);//文字列 s で初期化する

  • string(const string& str);//文字列オブジェクトを使用して別の文字列オブジェクトを初期化します

  • string(int n, char c);// n 文字で初期化します c

 例:

#include <string>
//string构造
void test01()
{
	string s1; //创建空字符串,调用无参构造函数
	cout << "str1 = " << s1 << endl;

	const char* str = "hello world";
	string s2(str); //把c_string转换成了string

	cout << "str2 = " << s2 << endl;

	string s3(s2); //调用拷贝构造函数
	cout << "str3 = " << s3 << endl;

	string s4(10, 'a');
	cout << "str3 = " << s3 << endl;
}

int main() {

	test01();

	system("pause");

	return 0;
}

要約: 文字列の複数の構築方法は比較できません。柔軟に使用してください。

3.1.3 文字列代入操作

機能の説明:

  • 文字列 string に値を代入する

代入の関数プロトタイプ:

  • string& operator=(const char* s);//Char*型の文字列を現在の文字列に代入する

  • string& operator=(const string &s);// 文字列 s を現在の文字列に代入します

  • string& operator=(char c);// 現在の文字列への文字の割り当て

  • string& assign(const char *s);// 文字列 s を現在の文字列に代入します

  • string& assign(const char *s, int n);//文字列 s の最初の n 文字を現在の文字列に代入します

  • string& assign(const string &s);// 文字列 s を現在の文字列に代入します

  • string& assign(int n, char c);// n 文字 c を現在の文字列に代入します

例:  

//赋值
void test01()
{
	string str1;
	str1 = "hello world";
	cout << "str1 = " << str1 << endl;

	string str2;
	str2 = str1;
	cout << "str2 = " << str2 << endl;

	string str3;
	str3 = 'a';
	cout << "str3 = " << str3 << endl;

	string str4;
	str4.assign("hello c++");
	cout << "str4 = " << str4 << endl;

	string str5;
	str5.assign("hello c++",5);
	cout << "str5 = " << str5 << endl;


	string str6;
	str6.assign(str5);
	cout << "str6 = " << str6 << endl;

	string str7;
	str7.assign(5, 'x');
	cout << "str7 = " << str7 << endl;
}

int main() {

	test01();

	system("pause");

	return 0;
}

要約:

文字列の代入方法はたくさんありますが、operator=この方法がより実用的です

 

3.1.4 文字列の連結

機能の説明:

  • 文字列の終端で文字列の接合を実現

関数プロトタイプ:

  • string& operator+=(const char* str);// += 演算子のオーバーロード

  • string& operator+=(const char c);// += 演算子のオーバーロード

  • string& operator+=(const string& str);// += 演算子のオーバーロード

  • string& append(const char *s);//文字列 s を現在の文字列の末尾に接続します

  • string& append(const char *s, int n);//文字列 s の最初の n 文字を現在の文字列の末尾に接続します

  • string& append(const string &s);//同演算子+=(const string& str)

  • string& append(const string &s, int pos, int n);//文字列 s 内の pos から始まる n 文字が文字列の末尾に接続されます

例:  

//字符串拼接
void test01()
{
	string str1 = "我";

	str1 += "爱玩游戏";

	cout << "str1 = " << str1 << endl;
	
	str1 += ':';

	cout << "str1 = " << str1 << endl;

	string str2 = "LOL DNF";

	str1 += str2;

	cout << "str1 = " << str1 << endl;

	string str3 = "I";
	str3.append(" love ");
	str3.append("game abcde", 4);
	//str3.append(str2);
	str3.append(str2, 4, 3); // 从下标4位置开始 ,截取3个字符,拼接到字符串末尾
	cout << "str3 = " << str3 << endl;
}
int main() {

	test01();

	system("pause");

	return 0;
}

概要: 文字列スプライシングにはオーバーロードされたバージョンが多数ありますが、初心者の段階でいくつか覚えておいてください。

3.1.5 文字列の検索と置換

機能の説明:

  • 検索: 指定された文字列が存在するかどうかを検索します。

  • 置換: 指定された位置の文字列を置換します。

関数プロトタイプ:

  • int find(const string& str, int pos = 0) const;// pos から開始して、最初に出現する str を検索します。

  • int find(const char* s, int pos = 0) const;// pos から始めて最初に出現する s を検索します

  • int find(const char* s, int pos, int n) const;// pos 位置から s の最初の n 文字の最初の位置を検索します

  • int find(const char c, int pos = 0) const;//最初に出現する文字 c を検索します。

  • int rfind(const string& str, int pos = npos) const;// pos から開始して str の最後の位置を検索します

  • int rfind(const char* s, int pos = npos) const;// pos から開始して、最後に出現する s を検索します。

  • int rfind(const char* s, int pos, int n) const;// pos から s の最初の n 文字の最後の位置を検索します

  • int rfind(const char c, int pos = 0) const;// 文字 c が最後に出現する箇所を検索します。

  • string& replace(int pos, int n, const string& str);//pos から始まる n 文字を文字列 str に置き換えます

  • string& replace(int pos, int n,const char* s);//pos から始まる n 文字を文字列 s に置き換えます

例:  

//查找和替换
void test01()
{
	//查找
	string str1 = "abcdefgde";

	int pos = str1.find("de");

	if (pos == -1)
	{
		cout << "未找到" << endl;
	}
	else
	{
		cout << "pos = " << pos << endl;
	}
	

	pos = str1.rfind("de");

	cout << "pos = " << pos << endl;

}

void test02()
{
	//替换
	string str1 = "abcdefgde";
	str1.replace(1, 3, "1111");//从1号位置其将三个字符替换为1111

	cout << "str1 = " << str1 << endl;
}

int main() {

	//test01();
	//test02();

	system("pause");

	return 0;
}

要約:

  • find は左から後ろに検索し、rfind は右から左に検索します。

  • find は、文字列を検索した後に見つかった最初の文字の位置を返し、見つからない場合は -1 を返します。

  • 置換する場合は、どの位置から、何文字、どのような文字列に置換するかを指定する必要があります

 

3.1.6 文字列の比較

機能の説明:

  • 文字列間の比較

比較する基準:

  • 文字列比較は文字の ASCII コードに基づいて行われます。

= 0 を返します

> 1 を返す

< は -1 を返します

関数プロトタイプ:

  • int compare(const string &s) const;// 文字列 s と比較します

  • int compare(const char *s) const;// 文字列 s と比較します

例:  

//字符串比较
void test01()
{

	string s1 = "hello";
	string s2 = "aello";

	int ret = s1.compare(s2);

	if (ret == 0) {
		cout << "s1 等于 s2" << endl;
	}
	else if (ret > 0)
	{
		cout << "s1 大于 s2" << endl;
	}
	else
	{
		cout << "s1 小于 s2" << endl;
	}

}

int main() {

	test01();

	system("pause");

	return 0;
}

要約: 文字列比較は主に 2 つの文字列が等しいかどうかを比較するために使用され、どちらが大きいか小さいかを判断することはあまり意味がありません。

3.1.7 文字列文字アクセス

文字列内の単一の文字にアクセスするには 2 つの方法があります

  • char& operator[](int n);//[]で文字を取得

  • char& at(int n);// at メソッドで文字を取得する

例:  

void test01()
{
	string str = "hello world";

	for (int i = 0; i < str.size(); i++)//size返回长度
	{
		cout << str[i] << " ";
	}
	cout << endl;

	for (int i = 0; i < str.size(); i++)
	{
		cout << str.at(i) << " ";
	}
	cout << endl;


	//字符修改
	str[0] = 'x';
	str.at(1) = 'x';
	cout << str << endl;
	
}

int main() {

	test01();

	system("pause");

	return 0;
}

概要: 文字列内の 1 文字にアクセスするには、[ ] または at を使用する 2 つの方法があります。

3.1.8 文字列の挿入と削除

機能の説明:

  • 文字列 string 上の文字の挿入と削除

関数プロトタイプ:

  • string& insert(int pos, const char* s);//文字列を挿入

  • string& insert(int pos, const string& str);//文字列を挿入

  • string& insert(int pos, int n, char c);//指定された位置に n 文字 c を挿入します

  • string& erase(int pos, int n = npos);//Pos から始まる n 文字を削除

 例:

//字符串插入和删除
void test01()
{
	string str = "hello";
	str.insert(1, "111");
	cout << str << endl;

	str.erase(1, 3);  //从1号位置开始3个字符
	cout << str << endl;
}

int main() {

	test01();

	system("pause");

	return 0;
}

概要:挿入と削除の開始添字は 0 から始まります

 

3.1.9 文字列の部分文字列

機能の説明:

  • 文字列から目的の部分文字列を取得します

関数プロトタイプ:

  • string substr(int pos = 0, int n = npos) const;// pos から始まる n 文字からなる文字列を返します

 例:

//子串
void test01()
{

	string str = "abcdefg";
	string subStr = str.substr(1, 3);
	cout << "subStr = " << subStr << endl;

	string email = "[email protected]";
	int pos = email.find("@");
	string username = email.substr(0, pos);
	cout << "username: " << username << endl;

}

int main() {

	test01();

	system("pause");

	return 0;
}

まとめ:部分文字列関数を柔軟に活用することで、実際の開発において有効な情報を取得できる

 

3.2 vector容器

3.2.1 ベクトルの基本概念

関数:

  • ベクトル データ構造は、シングルエンド配列とも呼ばれる配列に非常に似ています。

ベクトルと通常の配列の違い:

  • 違いは、配列が静的空間であり、ベクトルは動的に展開できることです。

動的拡張:

  • 元の領域の後に新しい領域を継続するのではなく、より大きなメモリ領域を見つけて、元のデータを新しい領域にコピーして元の領域を解放します。

  • ベクターコンテナのイテレータはランダムアクセスをサポートするイテレータです

3.2.2 ベクトルコンストラクター

機能の説明:

  • ベクターコンテナを作成する

関数プロトタイプ:

  • vector<T> v;//テンプレート実装クラス実装、デフォルトコンストラクタ

  • vector(v.begin(), v.end());// v[begin(), end()) の範囲内の要素をそれ自体にコピーします。

  • vector(n, elem);//コンストラクターは n 個の要素をそれ自体にコピーします。

  • vector(const vector &vec);// コンストラクターをコピーします。

例:  

#include <vector>

void printVector(vector<int>& v) {

	for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

void test01()
{
	vector<int> v1; //无参构造
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
	}
	printVector(v1);

	vector<int> v2(v1.begin(), v1.end());
	printVector(v2);

	vector<int> v3(10, 100);
	printVector(v3);
	
	vector<int> v4(v3);
	printVector(v4);
}

int main() {

	test01();

	system("pause");

	return 0;
}

要約:ベクトルの複数の構築方法は比較できません。柔軟に使用してください。

3.2.3 ベクトル代入演算

機能の説明:

  • ベクトルコンテナに値を代入する

関数プロトタイプ:

  • vector& operator=(const vector &vec);// 等号演算子をオーバーロードします

  • assign(beg, end);// 区間 [beg, end) 内のデータのコピーをそれ自体に割り当てます。

  • assign(n, elem);// n 個の要素のコピーをそれ自体に割り当てます。

 例:

#include <vector>

void printVector(vector<int>& v) {

	for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

//赋值操作
void test01()
{
	vector<int> v1; //无参构造
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
	}
	printVector(v1);

	vector<int>v2;
	v2 = v1;
	printVector(v2);

	vector<int>v3;
	v3.assign(v1.begin(), v1.end());
	printVector(v3);

	vector<int>v4;
	v4.assign(10, 100);
	printVector(v4);
}

int main() {

	test01();

	system("pause");

	return 0;
}

概要: ベクトルの代入方法は比較的単純で、operator= または assign を使用できます。

3.2.4 ベクトルの容量とサイズ

機能の説明:

  • ベクトルコンテナに対する静電容量とサイズの演算

関数プロトタイプ:

  • empty();// コンテナが空かどうかを確認する

  • capacity();//コンテナの容量

  • size();// コンテナ内の要素の数を返します

  • resize(int num);//コンテナの長さを num として再指定します。コンテナが長くなった場合は、新しい位置をデフォルト値で埋めます。

    // コンテナが短くなると、コンテナの長さを超える最後の要素が削除されます。

  • resize(int num, elem);//コンテナの長さを num として再指定します。コンテナが長くなった場合は、新しい位置を elem の値で埋めます。

    //コンテナが短くなった場合、コンテナの長さを超えた末尾の要素は削除されます

 例:

#include <vector>

void printVector(vector<int>& v) {

	for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

void test01()
{
	vector<int> v1;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
	}
	printVector(v1);
	if (v1.empty())//为真 代表容器为空
	{
		cout << "v1为空" << endl;
	}
	else
	{
		cout << "v1不为空" << endl;
		cout << "v1的容量 = " << v1.capacity() << endl;
		cout << "v1的大小 = " << v1.size() << endl;
	}

	//resize 重新指定大小 ,若指定的更大,默认用0填充新位置,可以利用重载版本替换默认填充
	v1.resize(15,10);
	printVector(v1);

	//resize 重新指定大小 ,若指定的更小,超出部分元素被删除
	v1.resize(5);
	printVector(v1);
}

int main() {

	test01();

	system("pause");

	return 0;
}

要約:

  • 空かどうかを判断します --- 空です

  • 要素の数を返します --- サイズ

  • 返却コンテナ容量 --- 容量

  • サイズを再指定 --- サイズ変更

3.2.5 ベクトルの挿入と削除

機能の説明:

  • ベクターコンテナに対する挿入および削除操作

関数プロトタイプ:

  • push_back(ele);//最後に要素eleを挿入

  • pop_back();// 最後の要素を削除します

  • insert(const_iterator pos, ele);//イテレータは要素 ele を挿入する位置 pos を指します。

  • insert(const_iterator pos, int count,ele);//イテレータは位置 pos を指し、count 要素 ele を挿入します。

  • erase(const_iterator pos);// イテレータが指す要素を削除します

  • erase(const_iterator start, const_iterator end);// イテレータの要素を最初から最後まで削除します。

  • clear();// コンテナ内のすべての要素を削除します

例:  


#include <vector>

void printVector(vector<int>& v) {

	for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

//插入和删除
void test01()
{
	vector<int> v1;
	//尾插
	v1.push_back(10);
	v1.push_back(20);
	v1.push_back(30);
	v1.push_back(40);
	v1.push_back(50);
	printVector(v1);
	//尾删
	v1.pop_back();
	printVector(v1);
	//插入
	v1.insert(v1.begin(), 100);//第一个是迭代器
	printVector(v1);

	v1.insert(v1.begin(), 2, 1000);
	printVector(v1);

	//删除
	v1.erase(v1.begin());
	printVector(v1);

	//清空
	v1.erase(v1.begin(), v1.end());
	v1.clear();
	printVector(v1);
}

int main() {

	test01();

	system("pause");

	return 0;
}

要約:

  • テールプラグ --- プッシュバック

  • 末尾削除 --- Pop_back

  • 挿入 --- 挿入 (位置反復子)

  • 削除 --- 消去 (位置反復子)

  • 空 --- クリア

3.2.6 ベクトルデータアクセス

機能の説明:

  • ベクター内のデータへのアクセス

関数プロトタイプ:

  • at(int idx);//インデックスidxが指すデータを返す

  • operator[];//インデックスidxが指すデータを返す

  • front();// コンテナ内の最初のデータ要素を返します

  • back();// コンテナ内の最後のデータ要素を返します

 例:

#include <vector>

void test01()
{
	vector<int>v1;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
	}

	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " ";
	}
	cout << endl;

	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1.at(i) << " ";
	}
	cout << endl;

	cout << "v1的第一个元素为: " << v1.front() << endl;
	cout << "v1的最后一个元素为: " << v1.back() << endl;
}

int main() {

	test01();

	system("pause");

	return 0;
}

要約:

  • イテレータを使用してベクトル コンテナ内の要素を取得することに加えて、[ ] および at を使用することもできます。

  • フロントはコンテナの最初の要素を返します

  • back はコンテナの最後の要素を返します

3.2.7 ベクトルスワップコンテナ

機能の説明:

  • 2 つのコンテナ内の要素の交換を実現するには

関数プロトタイプ:

  • swap(vec);// vec を独自の要素と交換します

 例:

#include <vector>

void printVector(vector<int>& v) {

	for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

void test01()
{
	vector<int>v1;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
	}
	printVector(v1);

	vector<int>v2;
	for (int i = 10; i > 0; i--)
	{
		v2.push_back(i);
	}
	printVector(v2);

	//互换容器
	cout << "互换后" << endl;
	v1.swap(v2);
	printVector(v1);
	printVector(v2);
}
//实际用途
//巧用swap可以收缩内存空间
void test02()
{
	vector<int> v;
	for (int i = 0; i < 100000; i++) {
		v.push_back(i);
	}

	cout << "v的容量为:" << v.capacity() << endl;//138255
	cout << "v的大小为:" << v.size() << endl;//100000

	v.resize(3);

	cout << "v的容量为:" << v.capacity() << endl;//138255
	cout << "v的大小为:" << v.size() << endl;//100000

	//收缩内存
	vector<int>(v).swap(v); //匿名对象

	cout << "v的容量为:" << v.capacity() << endl;//3
	cout << "v的大小为:" << v.size() << endl;//3
}

int main() {

	test01();

	test02();

	system("pause");

	return 0;
}

概要: スワップにより 2 つのコンテナーを交換可能にし、メモリを縮小するという実質的な効果を実現できます。

3.2.8 ベクトル予約スペース

機能の説明:

  • 動的に容量を拡張する際のベクターの拡張回数を削減

関数プロトタイプ:

  • reserve(int len);//コンテナは len 要素の長さを予約しますが、予約された位置は初期化されておらず、要素にはアクセスできません。

例:

#include <vector>

void test01()
{
	vector<int> v;

	//预留空间
	v.reserve(100000);

	int num = 0;//统计开辟次数
	int* p = NULL;
	for (int i = 0; i < 100000; i++) {
		v.push_back(i);
		if (p != &v[0]) {
			p = &v[0];
			num++;
		}
	}

	cout << "num:" << num << endl;
}

int main() {

	test01();
    
	system("pause");

	return 0;
}

概要: データ量が多い場合は、reserve を使用して最初にスペースを確保できます。

3.3 デックコンテナ

3.3.1 dequeコンテナの基本概念

関数:

  • ヘッドエンドで挿入および削除操作を実行できる両端アレイ

デックとベクターの違い:

  • 先頭のベクターの挿入・削除効率が低く、データ量が大きくなるほど効率が低下する

  • 比較的言えば、deque はベクターよりも速くヘッドの挿入と削除を行います。

  • Vector は deque よりも速く要素にアクセスします。これは 2 つの内部実装に関連しています。

deque の内部動作原理:

deque 内には中央コントローラーがあり、各バッファーの内容を維持し、実際のデータをバッファーに保存します。

中央コントローラーは各バッファーのアドレスを維持し、連続したメモリー空間のようにデキューを作成します。

  • deque コンテナーのイテレーターはランダムアクセスもサポートします 

3.3.2 両端キューコンストラクター

機能の説明:

  • デックコンテナの構築

関数プロトタイプ:

  • deque<T>deqT; //デフォルトの構築形式

  • deque(beg, end);//コンストラクターは範囲 [beg, end) 内の要素をそれ自体にコピーします。

  • deque(n, elem);//コンストラクターは n 個の要素をそれ自体にコピーします。

  • deque(const deque &deq);// コンストラクターをコピー

例:

#include <deque>

void printDeque(const deque<int>& d) 
{
	for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
		cout << *it << " ";

	}
	cout << endl;
}
//deque构造
void test01() {

	deque<int> d1; //无参构造函数
	for (int i = 0; i < 10; i++)
	{
		d1.push_back(i);
	}
	printDeque(d1);
	deque<int> d2(d1.begin(),d1.end());
	printDeque(d2);

	deque<int>d3(10,100);
	printDeque(d3);

	deque<int>d4 = d3;
	printDeque(d4);
}

int main() {

	test01();

	system("pause");

	return 0;
}

まとめ: dequeコンテナとvectorコンテナの構築方法はほぼ同じで柔軟に利用可能

3.3.3 デキュー代入操作

機能の説明:

  • DEQUE コンテナーに値を代入する

関数プロトタイプ:

  • deque& operator=(const deque &deq);// 等号演算子をオーバーロードします

  • assign(beg, end);// 区間 [beg, end) 内のデータのコピーをそれ自体に割り当てます。

  • assign(n, elem);// n 個の要素のコピーをそれ自体に割り当てます。

 例:

#include <deque>

void printDeque(const deque<int>& d) 
{
	for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
		cout << *it << " ";

	}
	cout << endl;
}
//赋值操作
void test01()
{
	deque<int> d1;
	for (int i = 0; i < 10; i++)
	{
		d1.push_back(i);
	}
	printDeque(d1);

	deque<int>d2;
	d2 = d1;
	printDeque(d2);

	deque<int>d3;
	d3.assign(d1.begin(), d1.end());
	printDeque(d3);

	deque<int>d4;
	d4.assign(10, 100);
	printDeque(d4);

}

int main() {

	test01();

	system("pause");

	return 0;
}

要約: デキュー代入操作もベクトルと同じであり、習得する必要があります。

3.3.4 デキューサイズの操作

機能の説明:

  • deque コンテナーのサイズを操作する

関数プロトタイプ:

  • deque.empty();// コンテナが空かどうかを確認する

  • deque.size();// コンテナ内の要素の数を返します

  • deque.resize(num);//コンテナの長さを num として再指定します。コンテナが長くなった場合は、新しい位置をデフォルト値で埋めます。

                                     // コンテナが短くなると、コンテナの長さを超える最後の要素が削除されます。

  • deque.resize(num, elem);//コンテナの長さを num として再指定します。コンテナが長くなった場合は、新しい位置を elem の値で埋めます。

                                            // コンテナが短くなると、コンテナの長さを超える最後の要素が削除されます。

 例:

#include <deque>

void printDeque(const deque<int>& d) 
{
	for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
		cout << *it << " ";

	}
	cout << endl;
}

//大小操作
void test01()
{
	deque<int> d1;
	for (int i = 0; i < 10; i++)
	{
		d1.push_back(i);
	}
	printDeque(d1);

	//判断容器是否为空
	if (d1.empty()) {
		cout << "d1为空!" << endl;
	}
	else {
		cout << "d1不为空!" << endl;
		//统计大小
		cout << "d1的大小为:" << d1.size() << endl;
	}

	//重新指定大小
	d1.resize(15, 1);
	printDeque(d1);

	d1.resize(5);
	printDeque(d1);
}

int main() {

	test01();

	system("pause");

	return 0;
}

要約:

  • Deque には容量の概念がありません

  • 空かどうかを判断します --- 空です

  • 要素の数を返します --- サイズ

  • 番号を再指定 --- サイズ変更

3.3.5 deque の挿入と削除

機能の説明:

  • DEQUE コンテナーへのデータの挿入と削除

関数プロトタイプ:

両端での挿入操作:

  • push_back(elem);//コンテナの最後にデータを追加します

  • push_front(elem);//コンテナの先頭にデータを挿入

  • pop_back();//コンテナ内の最後のデータを削除します

  • pop_front();//コンテナ内の最初のデータを削除します

指定位置動作:

  • insert(pos,elem);// elem 要素のコピーを pos の位置に挿入し、新しいデータの位置を返します。

  • insert(pos,n,elem);//位置 pos に n elem データを挿入します。戻り値はありません。

  • insert(pos,beg,end);//[beg,end) 間隔の位置 pos にデータを挿入します。戻り値はありません。

  • clear();// コンテナ内のすべてのデータをクリアします

  • erase(beg,end);//区間[beg, end)のデータを削除し、次のデータの位置を返します。

  • erase(pos);//pos 位置のデータを削除し、次のデータの位置を返します。

 例:

#include <deque>

void printDeque(const deque<int>& d) 
{
	for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
		cout << *it << " ";

	}
	cout << endl;
}
//两端操作
void test01()
{
	deque<int> d;
	//尾插
	d.push_back(10);
	d.push_back(20);
	//头插
	d.push_front(100);
	d.push_front(200);

	printDeque(d);

	//尾删
	d.pop_back();
	//头删
	d.pop_front();
	printDeque(d);
}

//插入
void test02()
{
	deque<int> d;
	d.push_back(10);
	d.push_back(20);
	d.push_front(100);
	d.push_front(200);
	printDeque(d);

	d.insert(d.begin(), 1000);
	printDeque(d);

	d.insert(d.begin(), 2,10000);
	printDeque(d);

	deque<int>d2;
	d2.push_back(1);
	d2.push_back(2);
	d2.push_back(3);

	d.insert(d.begin(), d2.begin(), d2.end());
	printDeque(d);

}

//删除
void test03()
{
	deque<int> d;
	d.push_back(10);
	d.push_back(20);
	d.push_front(100);
	d.push_front(200);
	printDeque(d);

	d.erase(d.begin());
	printDeque(d);

	d.erase(d.begin(), d.end());
	d.clear();
	printDeque(d);
}

int main() {

	//test01();

	//test02();

    test03();
    
	system("pause");

	return 0;
}

要約:

  • 挿入と削除のために提供される位置はイテレータです。

  • テールプラグ --- プッシュバック

  • 末尾削除 --- Pop_back

  • ヘッドプラグ --- Push_front

  • 先頭削除 --- Pop_front

3.3.6 データアクセスのデキュー

機能の説明:

  • デキュー内のデータへのアクセス

関数プロトタイプ:

  • at(int idx);//インデックスidxが指すデータを返す

  • operator[];//インデックスidxが指すデータを返す

  • front();// コンテナ内の最初のデータ要素を返します

  • back();// コンテナ内の最後のデータ要素を返します

 例:

#include <deque>

void printDeque(const deque<int>& d) 
{
	for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
		cout << *it << " ";

	}
	cout << endl;
}

//数据存取
void test01()
{

	deque<int> d;
	d.push_back(10);
	d.push_back(20);
	d.push_front(100);
	d.push_front(200);

	for (int i = 0; i < d.size(); i++) {
		cout << d[i] << " ";
	}
	cout << endl;


	for (int i = 0; i < d.size(); i++) {
		cout << d.at(i) << " ";
	}
	cout << endl;

	cout << "front:" << d.front() << endl;

	cout << "back:" << d.back() << endl;

}

int main() {

	test01();

	system("pause");

	return 0;
}

要約:

  • イテレータを使用して両端キュー コンテナ内の要素を取得することに加えて、[ ] と at を使用することもできます。

  • フロントはコンテナの最初の要素を返します

  • back はコンテナの最後の要素を返します

3.3.7 デックソート

機能の説明:

  • アルゴリズムを使用して両端キュー コンテナを並べ替える

アルゴリズム:

  • sort(iterator beg, iterator end)// 要素を開始間隔と終了間隔 (デフォルトの昇順) で並べ替えます。

ランダム アクセス反復子をサポートするコンテナーの場合、ソート アルゴリズムを使用してコンテナーを直接ソートできます。 

ベクターコンテナはsortを使用して並べ替えることもできます

 例:

#include <deque>
#include <algorithm>

void printDeque(const deque<int>& d) 
{
	for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
		cout << *it << " ";

	}
	cout << endl;
}

void test01()
{

	deque<int> d;
	d.push_back(10);
	d.push_back(20);
	d.push_front(100);
	d.push_front(200);

	printDeque(d);
	sort(d.begin(), d.end());
	printDeque(d);

}

int main() {

	test01();

	system("pause");

	return 0;
}

要約: ソート アルゴリズムは非常に実用的です。使用する場合はヘッダー ファイル アルゴリズムを含めるだけです。

3.4 事件 - 裁判官の採点

3.4.1 ケースの説明

出場者は 5 人です: 出場者 ABCDE、10 人の審査員が各出場者を個別に採点し、最高得点を削除し、審査員の中で最低得点を削除し、平均得点を取得します。

3.4.2 実装手順

  1. 5 人のプレーヤーを作成し、ベクターに配置します。

  2. ベクター コンテナを走査し、各プレイヤーを取り出し、for ループを実行し、deque コンテナに 10 個の評価を保存します。

  3. 並べ替えアルゴリズムは、両端キュー コンテナ内のスコアを並べ替えて、最高スコアと最低スコアを削除します。

  4. Deque コンテナーは 1 回トラバースし、合計スコアを蓄積します。

  5. 平均点を得る

サンプルコード:  

//选手类
class Person
{
public:
	Person(string name, int score)
	{
		this->m_Name = name;
		this->m_Score = score;
	}

	string m_Name; //姓名
	int m_Score;  //平均分
};

void createPerson(vector<Person>&v)
{
	string nameSeed = "ABCDE";
	for (int i = 0; i < 5; i++)
	{
		string name = "选手";
		name += nameSeed[i];

		int score = 0;

		Person p(name, score);

		//将创建的person对象 放入到容器中
		v.push_back(p);
	}
}

//打分
void setScore(vector<Person>&v)
{
	for (vector<Person>::iterator it = v.begin(); it != v.end(); it++)
	{
		//将评委的分数 放入到deque容器中
		deque<int>d;
		for (int i = 0; i < 10; i++)
		{
			int score = rand() % 41 + 60;  // 60 ~ 100
			d.push_back(score);
		}

		//cout << "选手: " << it->m_Name << " 打分: " << endl;
		//for (deque<int>::iterator dit = d.begin(); dit != d.end(); dit++)
		//{
		//	cout << *dit << " ";
		//}
		//cout << endl;

		//排序
		sort(d.begin(), d.end());

		//去除最高和最低分
		d.pop_back();
		d.pop_front();

		//取平均分
		int sum = 0;
		for (deque<int>::iterator dit = d.begin(); dit != d.end(); dit++)
		{
			sum += *dit; //累加每个评委的分数
		}

		int avg = sum / d.size();

		//将平均分 赋值给选手身上
		it->m_Score = avg;
	}

}

void showScore(vector<Person>&v)
{
	for (vector<Person>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << "姓名: " << it->m_Name << " 平均分: " << it->m_Score << endl;
	}
}

int main() {

	//随机数种子
	srand((unsigned int)time(NULL));

	//1、创建5名选手
	vector<Person>v;  //存放选手容器
	createPerson(v);

	//测试
	//for (vector<Person>::iterator it = v.begin(); it != v.end(); it++)
	//{
	//	cout << "姓名: " << (*it).m_Name << " 分数: " << (*it).m_Score << endl;
	//}

	//2、给5名选手打分
	setScore(v);

	//3、显示最后得分
	showScore(v);

	system("pause");

	return 0;
}

概要:データを操作するためにさまざまなコンテナーを選択すると、コードの効率が向上します。

3.5スタックコンテナ

3.5.1 スタックの基本概念

概念:スタックは、出口が 1 つだけある先入れ後出し(FILO) データ構造です。

スタック内の最上位の要素のみが外部から使用できるため、スタックではトラバーサル動作が許可されません。

スタックにデータを入力することを「スタッキング」と呼びます push

スタックからのデータのポップは、「スタックのポップ」と呼ばれます。 pop

人生の積み重ね:  

 

 

3.5.2 スタック共通インターフェース

機能説明: スタックコンテナのよく使われる外部インターフェース

コンストラクタ:

  • stack<T> stk;//スタックはテンプレート クラスとスタック オブジェクトのデフォルトの構築形式を使用して実装されます

  • stack(const stack &stk);// コンストラクターをコピー

代入操作:

  • stack& operator=(const stack &stk);// 等号演算子をオーバーロードします

データアクセス:

  • push(elem);// 要素をスタックの先頭に追加します

  • pop();//スタックの先頭から最初の要素を削除します

  • top();// スタックの最上位要素を返す

サイズ操作:

  • empty();// スタックが空かどうかを確認する

  • size();// スタックのサイズを返す

例:  

#include <stack>

//栈容器常用接口
void test01()
{
	//创建栈容器 栈容器必须符合先进后出
	stack<int> s;

	//向栈中添加元素,叫做 压栈 入栈
	s.push(10);
	s.push(20);
	s.push(30);

	while (!s.empty()) {
		//输出栈顶元素
		cout << "栈顶元素为: " << s.top() << endl;
		//弹出栈顶元素
		s.pop();
	}
	cout << "栈的大小为:" << s.size() << endl;

}

int main() {

	test01();

	system("pause");

	return 0;
}

要約:

  • スタックへ --- プッシュ

  • ポップアウト --- ポップ

  • スタックの先頭に戻る --- トップ

  • スタックが空かどうかを判断します --- 空です

  • 戻りスタック サイズ --- サイズ

3.6 キューコンテナ

3.6.1 キューの基本概念

概念:キューは先入れ先出し (FIFO) データ構造であり 2 つの出口があります。

キュー コンテナでは、一方の端から要素を追加し、もう一方の端から要素を削除できます。

キューの先頭と末尾のみが外部から使用できるため、キューのトラバーサル動作は許可されません

キューにデータを入力することを「キューに入る」と呼びます。 push

キューからのデータは --- out of the queueと呼ばれます pop

人生のキュー:  

3.6.2 キュー共通インターフェース

機能説明: スタックコンテナのよく使われる外部インターフェース

コンストラクタ:

  • queue<T> que;//キューはテンプレート クラスとキュー オブジェクトのデフォルトの構築形式によって実装されます

  • queue(const queue &que);// コンストラクターをコピー

代入操作:

  • queue& operator=(const queue &que);// 等号演算子をオーバーロードします

データアクセス:

  • push(elem);// 要素をキューの最後に追加します

  • pop();// キューの先頭から最初の要素を削除します

  • back();// 最後の要素を返す

  • front();// 最初の要素を返す

サイズ操作:

  • empty();// スタックが空かどうかを確認する

  • size();// スタックのサイズを返す

 例:

#include <queue>
#include <string>
class Person
{
public:
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}

	string m_Name;
	int m_Age;
};

void test01() {

	//创建队列
	queue<Person> q;

	//准备数据
	Person p1("唐僧", 30);
	Person p2("孙悟空", 1000);
	Person p3("猪八戒", 900);
	Person p4("沙僧", 800);

	//向队列中添加元素  入队操作
	q.push(p1);
	q.push(p2);
	q.push(p3);
	q.push(p4);

	//队列不提供迭代器,更不支持随机访问	
	while (!q.empty()) {
		//输出队头元素
		cout << "队头元素-- 姓名: " << q.front().m_Name 
              << " 年龄: "<< q.front().m_Age << endl;
        
		cout << "队尾元素-- 姓名: " << q.back().m_Name  
              << " 年龄: " << q.back().m_Age << endl;
        
		cout << endl;
		//弹出队头元素
		q.pop();
	}

	cout << "队列大小为:" << q.size() << endl;
}

int main() {

	test01();

	system("pause");

	return 0;
}

要約:

  • エンキュー --- プッシュ

  • デキュー --- ポップ

  • head 要素を返します --- 前

  • キューの最後にある要素を返します --- back

  • チームが空かどうかを判断します --- 空です

  • 戻りキューのサイズ --- サイズ

3.7 リストコンテナ

3.7.1 リストの基本概念

機能:データのチェーンストレージ

リンク リスト(リスト) は物理ストレージ ユニット上の不連続なストレージ構造であり、データ要素の論理的順序はリンク リスト内のポインタ リンクを通じて実現されます。

リンク リストの構成: リンク リストは一連のノードで構成されます。

ノードの構成: 1 つはデータ要素を格納するデータ フィールド、もう 1 つは次のノードのアドレスを格納するポインタ フィールドです。

STL のリンク リストは二重循環リンク リストです。

 リンク リストの格納方法は連続したメモリ空間ではないため、リンク リスト リスト内のイテレータは、双方向イテレータである前方および後方への移動のみをサポートします。

リストの利点:

  • 動的ストレージ割り当てを使用すると、メモリの無駄やオーバーフローが発生しません。

  • リンクされたリストは、多数の要素を移動せずにポインタを変更するだけで、挿入および削除操作を実行するのに非常に便利です。

リストの欠点:

  • リンクされたリストは柔軟ですが、スペース (ポインター フィールド) と時間 (トラバース) が余分にかかります。

List には重要なプロパティがあり、挿入および削除操作によって元のリスト反復子が無効になることはありませんが、これは Vector では当てはまりません。

概要: STL で最もよく使用される 2 つのコンテナーはリストとベクターであり、それぞれに独自の長所と短所があります。

3.7.2 リストコンストラクター

機能の説明:

  • リストコンテナを作成する

関数プロトタイプ:

  • list<T> lst;//リストはテンプレート クラスとオブジェクトのデフォルトの構築形式を使用して実装されます。

  • list(beg,end);//コンストラクターは範囲 [beg, end) 内の要素をそれ自体にコピーします。

  • list(n,elem);//コンストラクターは n 個の要素をそれ自体にコピーします。

  • list(const list &lst);// コンストラクターをコピーします。

 例:

#include <list>

void printList(const list<int>& L) {

	for (list<int>::const_iterator it = L.begin(); it != L.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

void test01()
{
	list<int>L1;
	L1.push_back(10);
	L1.push_back(20);
	L1.push_back(30);
	L1.push_back(40);

	printList(L1);

	list<int>L2(L1.begin(),L1.end());
	printList(L2);

	list<int>L3(L2);
	printList(L3);

	list<int>L4(10, 1000);
	printList(L4);
}

int main() {

	test01();

	system("pause");

	return 0;
}

概要: リストの構築方法は、他のいくつかの一般的に使用される STL コンテナーの方法と同じです。よく理解しておくだけです。

3.7.3 リストの割り当てと交換

機能の説明:

  • リストコンテナと交換リストコンテナに値を割り当てる

関数プロトタイプ:

  • assign(beg, end);// 区間 [beg, end) 内のデータのコピーをそれ自体に割り当てます。

  • assign(n, elem);// n 個の要素のコピーをそれ自体に割り当てます。

  • list& operator=(const list &lst);// 等号演算子をオーバーロードします

  • swap(lst);// lst を独自の要素と交換します。

 例:

#include <list>

void printList(const list<int>& L) {

	for (list<int>::const_iterator it = L.begin(); it != L.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

//赋值和交换
void test01()
{
	list<int>L1;
	L1.push_back(10);
	L1.push_back(20);
	L1.push_back(30);
	L1.push_back(40);
	printList(L1);

	//赋值
	list<int>L2;
	L2 = L1;
	printList(L2);

	list<int>L3;
	L3.assign(L2.begin(), L2.end());
	printList(L3);

	list<int>L4;
	L4.assign(10, 100);
	printList(L4);

}

//交换
void test02()
{

	list<int>L1;
	L1.push_back(10);
	L1.push_back(20);
	L1.push_back(30);
	L1.push_back(40);

	list<int>L2;
	L2.assign(10, 100);

	cout << "交换前: " << endl;
	printList(L1);
	printList(L2);

	cout << endl;

	L1.swap(L2);

	cout << "交换后: " << endl;
	printList(L1);
	printList(L2);

}

int main() {

	//test01();

	test02();

	system("pause");

	return 0;
}

まとめ: リストの割り当てと交換操作を柔軟に使用できる

3.7.4 リストサイズの操作

機能の説明:

  • リストコンテナのサイズを操作する

関数プロトタイプ:

  • size();// コンテナ内の要素の数を返します

  • empty();// コンテナが空かどうかを確認する

  • resize(num);//コンテナの長さを num として再指定します。コンテナが長くなった場合は、新しい位置をデフォルト値で埋めます。

                          // コンテナが短くなると、コンテナの長さを超える最後の要素が削除されます。

  • resize(num, elem);//コンテナの長さを num として再指定します。コンテナが長くなった場合は、新しい位置を elem の値で埋めます。

    //コンテナが短くなった場合、コンテナの長さを超えた末尾の要素は削除されます。

 例:

#include <list>

void printList(const list<int>& L) {

	for (list<int>::const_iterator it = L.begin(); it != L.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

//大小操作
void test01()
{
	list<int>L1;
	L1.push_back(10);
	L1.push_back(20);
	L1.push_back(30);
	L1.push_back(40);

	if (L1.empty())
	{
		cout << "L1为空" << endl;
	}
	else
	{
		cout << "L1不为空" << endl;
		cout << "L1的大小为: " << L1.size() << endl;
	}

	//重新指定大小
	L1.resize(10);
	printList(L1);

	L1.resize(2);
	printList(L1);
}

int main() {

	test01();

	system("pause");

	return 0;
}

要約:

  • 空かどうかを判断します --- 空です

  • 要素の数を返します --- サイズ

  • 番号を再指定 --- サイズ変更

3.7.5 リストの挿入と削除

機能の説明:

  • リストコンテナへのデータの挿入と削除

関数プロトタイプ:

  • Push_back(elem);//コンテナの最後に要素を追加します

  • Pop_back();//コンテナ内の最後の要素を削除します

  • Push_front(elem);//コンテナの先頭に要素を挿入します

  • Pop_front();//コンテナの先頭から最初の要素を削除します

  • insert(pos,elem);//elem 要素のコピーを pos 位置に挿入し、新しいデータの位置を返します。

  • insert(pos,n,elem);//位置 pos に n 個の elem データを挿入します。戻り値はありません。

  • insert(pos,beg,end);//[beg,end) 範囲のデータを位置 pos に挿入します。戻り値はありません。

  • clear();//コンテナ内のすべてのデータを削除します

  • Erase(beg,end);//[beg,end)範囲のデータを削除し、次のデータの位置を返します。

  • Erase(pos);//pos 位置のデータを削除し、次のデータの位置を返します。

  • Remove(elem);//elem の値に一致するコンテナ内のすべての要素を削除します。

 例:

#include <list>

void printList(const list<int>& L) {

	for (list<int>::const_iterator it = L.begin(); it != L.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

//插入和删除
void test01()
{
	list<int> L;
	//尾插
	L.push_back(10);
	L.push_back(20);
	L.push_back(30);
	//头插
	L.push_front(100);
	L.push_front(200);
	L.push_front(300);

	printList(L);

	//尾删
	L.pop_back();
	printList(L);

	//头删
	L.pop_front();
	printList(L);

	//插入
	list<int>::iterator it = L.begin();
	L.insert(++it, 1000);
	printList(L);

	//删除
	it = L.begin();
	L.erase(++it);
	printList(L);

	//移除
	L.push_back(10000);
	L.push_back(10000);
	L.push_back(10000);
	printList(L);
	L.remove(10000);
	printList(L);
    
    //清空
	L.clear();
	printList(L);
}

int main() {

	test01();

	system("pause");

	return 0;
}

要約:

  • テールプラグ --- プッシュバック

  • 末尾削除 --- Pop_back

  • ヘッドプラグ --- Push_front

  • 先頭削除 --- Pop_front

  • 挿入 --- 挿入

  • 削除 --- 消去

  • 削除 --- 削除

  • 空 --- クリア

 

3.7.6 リストデータアクセス

機能の説明:

  • リストコンテナ内のデータにアクセスする

関数プロトタイプ:

  • front();// 最初の要素を返します。

  • back();// 最後の要素を返します。

 例:

#include <list>

//数据存取
void test01()
{
	list<int>L1;
	L1.push_back(10);
	L1.push_back(20);
	L1.push_back(30);
	L1.push_back(40);

	
	//cout << L1.at(0) << endl;//错误 不支持at访问数据
	//cout << L1[0] << endl; //错误  不支持[]方式访问数据
	cout << "第一个元素为: " << L1.front() << endl;
	cout << "最后一个元素为: " << L1.back() << endl;

	//list容器的迭代器是双向迭代器,不支持随机访问
	list<int>::iterator it = L1.begin();
    it++;//支持随机访问
    it--;//支持双向
	//it = it + 1;//错误,不可以跳跃访问,即使是+1
}

int main() {

	test01();

	system("pause");

	return 0;
}

要約:

  • リスト コンテナー内の [] または at を介してデータにアクセスすることはできません

  • 最初の要素を返します --- フロント

  • 最後の要素を返します --- back

 

3.7.7 リストの反転とソート

機能の説明:

  • コンテナ内の要素を反転し、コンテナ内のデータを並べ替えます。

ランダム アクセス イテレータをサポートしていないすべてのコンテナは、標準アルゴリズム sort() を使用できません。

ランダム アクセス イテレータをサポートしないコンテナは、対応するアルゴリズム L.sort() を内部的に提供します。

関数プロトタイプ:

  • reverse();// 逆リンクリスト

  • sort();//リストのソート

 例:

void printList(const list<int>& L) {

	for (list<int>::const_iterator it = L.begin(); it != L.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

bool myCompare(int val1 , int val2)
{
	return val1 > val2;//降序,第一个数>第二个数
}

//反转和排序
void test01()
{
	list<int> L;
	L.push_back(90);
	L.push_back(30);
	L.push_back(20);
	L.push_back(70);
	printList(L);

	//反转容器的元素
	L.reverse();
	printList(L);

	//排序
    //所有不支持随机访问迭代器的容器,不可以用标准算法  sort()
    //不支持随机访问迭代器的容器,内部会提供对应一些算法  L.sort()
	L.sort(); //默认的排序规则 从小到大
	printList(L);

	L.sort(myCompare); //指定规则,从大到小
	printList(L);
}

int main() {

	test01();

	system("pause");

	return 0;
}

要約:

  • リバース --- リバース

  • 並べ替え --- sort (メンバー関数)

 

3.7.8 ケースのソート

ケースの説明: 人物のカスタム データ型を並べ替えます。人物の属性には名前、年齢、身長が含まれます。

並べ替えルール: 年齢による昇順、年齢が同じ場合は身長による降順

 例:

#include <list>
#include <string>
class Person {
public:
	Person(string name, int age , int height) {
		m_Name = name;
		m_Age = age;
		m_Height = height;
	}

public:
	string m_Name;  //姓名
	int m_Age;      //年龄
	int m_Height;   //身高
};


bool ComparePerson(Person& p1, Person& p2) {

	if (p1.m_Age == p2.m_Age) {
		return p1.m_Height  > p2.m_Height;
	}
	else
	{
		return  p1.m_Age < p2.m_Age;
	}

}

void test01() {

	list<Person> L;

	Person p1("刘备", 35 , 175);
	Person p2("曹操", 45 , 180);
	Person p3("孙权", 40 , 170);
	Person p4("赵云", 25 , 190);
	Person p5("张飞", 35 , 160);
	Person p6("关羽", 35 , 200);

	L.push_back(p1);
	L.push_back(p2);
	L.push_back(p3);
	L.push_back(p4);
	L.push_back(p5);
	L.push_back(p6);

	for (list<Person>::iterator it = L.begin(); it != L.end(); it++) {
		cout << "姓名: " << it->m_Name << " 年龄: " << it->m_Age 
              << " 身高: " << it->m_Height << endl;
	}

	cout << "---------------------------------" << endl;
	L.sort(ComparePerson); //排序

	for (list<Person>::iterator it = L.begin(); it != L.end(); it++) {
		cout << "姓名: " << it->m_Name << " 年龄: " << it->m_Age 
              << " 身高: " << it->m_Height << endl;
	}
}

int main() {

	test01();

	system("pause");

	return 0;
}

要約:

  • カスタム データ型の場合、照合順序を指定する必要があります。指定しない場合、コンパイラは並べ替え方法を認識できません。

  • 高度な並べ替えは、並べ替えルールに関する論理ルールの定式化の 1 つであり、複雑ではありません。

 

3.8 set/ multiset 容器

3.8.1 集合の基本概念

序章:

  • すべての要素は挿入時に自動的にソートされます

自然:

  • set/multiset は連想コンテナであり、基礎となる構造はバイナリ ツリーで実装されます。

セットとマルチセットの違い:

  • set ではコンテナ内で要素の重複を許可しません

  • マルチセットではコンテナ内で重複した要素が許可されます

3.8.2 セットの構築と代入

関数の説明: セットコンテナを作成し、値を割り当てる

構造:

  • set<T> st;//デフォルトのコンストラクター:

  • set(const set &st);// コンストラクターをコピー

割り当て:

  • set& operator=(const set &st);// 等号演算子をオーバーロードします

例:

#include <set>

void printSet(set<int> & s)
{
	for (set<int>::iterator it = s.begin(); it != s.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

//构造和赋值
void test01()
{
	set<int> s1;

	s1.insert(10);
	s1.insert(30);
	s1.insert(20);
	s1.insert(40);
	printSet(s1);

	//拷贝构造
	set<int>s2(s1);
	printSet(s2);

	//赋值
	set<int>s3;
	s3 = s2;
	printSet(s3);
}

int main() {

	test01();

	system("pause");

	return 0;
}

要約:

  • セットコンテナにデータを挿入する場合は、insert を使用します。

  • 設定したコンテナに挿入されたデータは自動的にソートされます

3.8.3 サイズとスワップの設定

機能の説明:

  • 統計セットコンテナのサイズと交換セットコンテナ

関数プロトタイプ:

  • size();// コンテナ内の要素の数を返します

  • empty();// コンテナが空かどうかを確認する

  • swap(st);// 2 つのコレクション コンテナを交換します

例:

#include <set>

void printSet(set<int> & s)
{
	for (set<int>::iterator it = s.begin(); it != s.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

//大小
void test01()
{

	set<int> s1;
	
	s1.insert(10);
	s1.insert(30);
	s1.insert(20);
	s1.insert(40);

	if (s1.empty())
	{
		cout << "s1为空" << endl;
	}
	else
	{
		cout << "s1不为空" << endl;
		cout << "s1的大小为: " << s1.size() << endl;
	}

}

//交换
void test02()
{
	set<int> s1;

	s1.insert(10);
	s1.insert(30);
	s1.insert(20);
	s1.insert(40);

	set<int> s2;

	s2.insert(100);
	s2.insert(300);
	s2.insert(200);
	s2.insert(400);

	cout << "交换前" << endl;
	printSet(s1);
	printSet(s2);
	cout << endl;

	cout << "交换后" << endl;
	s1.swap(s2);
	printSet(s1);
	printSet(s2);
}

int main() {

	//test01();

	test02();

	system("pause");

	return 0;
}

要約:

  • 統計サイズ --- サイズ

  • 空かどうかを判断します --- 空です

  • コンテナを交換 --- スワップ

 

3.8.4 セットの挿入と削除

機能の説明:

  • データの挿入とデータの削除を行うコンテナを設定します

関数プロトタイプ:

  • insert(elem);// 要素をコンテナに挿入します。

  • clear();//すべての要素をクリア

  • erase(pos);// pos イテレータが指す要素を削除し、次の要素のイテレータを返します。

  • erase(beg, end);//区間 [beg, end) 内のすべての要素を削除し、次の要素のイテレータを返します。

  • erase(elem);//コンテナ内の値が elem である要素を削除します。

 例:

#include <set>

void printSet(set<int> & s)
{
	for (set<int>::iterator it = s.begin(); it != s.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

//插入和删除
void test01()
{
	set<int> s1;
	//插入
	s1.insert(10);
	s1.insert(30);
	s1.insert(20);
	s1.insert(40);
	printSet(s1);

	//删除
	s1.erase(s1.begin());
	printSet(s1);

	s1.erase(30);
	printSet(s1);

	//清空
	//s1.erase(s1.begin(), s1.end());
	s1.clear();
	printSet(s1);
}

int main() {

	test01();

	system("pause");

	return 0;
}

要約:

  • 挿入 --- 挿入

  • 削除 --- 消去

  • 空 --- クリア

3.8.5 セットの検索と統計

機能の説明:

  • セットされたコンテナに関するデータと統計を検索します

関数プロトタイプ:

  • find(key);// キーが存在するかどうかを調べ、存在する場合はキーの要素のイテレータを返し、存在しない場合は set.end() を返します。

  • count(key);// キー内の要素の数をカウントします。

例:

#include <set>

//查找和统计
void test01()
{
	set<int> s1;
	//插入
	s1.insert(10);
	s1.insert(30);
	s1.insert(20);
	s1.insert(40);
	
	//查找
	set<int>::iterator pos = s1.find(30);

	if (pos != s1.end())
	{
		cout << "找到了元素 : " << *pos << endl;
	}
	else
	{
		cout << "未找到元素" << endl;
	}

	//统计
	int num = s1.count(30);
	cout << "num = " << num << endl;
}

int main() {

	test01();

	system("pause");

	return 0;
}

 

要約:

  • Find --- find (反復子を返す)

  • 統計 --- count (セットの場合、結果は 0 または 1)

3.8.6 セットとマルチセットの違い

学習目標:

  • セットとマルチセットの違いをマスターする

違い:

  • セットでは重複データを挿入できませんが、マルチセットでは重複データを挿入できます。

  • set がデータを挿入すると、挿入が成功したかどうかを示す挿入結果が返されます。

  • マルチセットはデータを検出しないため、重複したデータが挿入される可能性があります

例:

#include <set>

//set和multiset区别
void test01()
{
	set<int> s;
	pair<set<int>::iterator, bool>  ret = s.insert(10);
	if (ret.second) {
		cout << "第一次插入成功!" << endl;
	}
	else {
		cout << "第一次插入失败!" << endl;
	}

	ret = s.insert(10);
	if (ret.second) {
		cout << "第二次插入成功!" << endl;
	}
	else {
		cout << "第二次插入失败!" << endl;
	}
    
	//multiset
	multiset<int> ms;
	ms.insert(10);
	ms.insert(10);

	for (multiset<int>::iterator it = ms.begin(); it != ms.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

int main() {

	test01();

	system("pause");

	return 0;
}

 

要約:

  • 重複データの挿入が許可されていない場合は、set を使用できます。

  • 重複したデータを挿入する必要がある場合は、multiset を使用してください

3.8.7 ペアの作成

機能の説明:

  • ペアで表示されるデータ。ペアを使用すると 2 つのデータを返すことができます

作成する 2 つの方法:

  • pair<type, type> p ( value1, value2 );

  • pair<type, type> p = make_pair( value1, value2 );

例:

#include <string>

//对组创建
void test01()
{
	pair<string, int> p(string("Tom"), 20);
	cout << "姓名: " <<  p.first << " 年龄: " << p.second << endl;

	pair<string, int> p2 = make_pair("Jerry", 10);
	cout << "姓名: " << p2.first << " 年龄: " << p2.second << endl;
}

int main() {

	test01();

	system("pause");

	return 0;
}

要約:

どちらの方法でもペアを作成できますが、1 つだけ覚えておいてください

 

3.8.8 コンテナのソートを設定する

学習目標:

  • 設定されたコンテナのデフォルトのソートルールは小さいものから大きいものまでです。ソートルールの変更方法をマスターしてください

主な技術的なポイント:

  • ファンクターを使用すると、並べ替えルールを変更できます

例 1セットは組み込みデータ型を格納します

#include <set>

class MyCompare 
{
public:
	bool operator()(int v1, int v2) {
		return v1 > v2;
	}
};
void test01() 
{    
	set<int> s1;
	s1.insert(10);
	s1.insert(40);
	s1.insert(20);
	s1.insert(30);
	s1.insert(50);

	//默认从小到大
	for (set<int>::iterator it = s1.begin(); it != s1.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;

	//指定排序规则
	set<int,MyCompare> s2;
	s2.insert(10);
	s2.insert(40);
	s2.insert(20);
	s2.insert(30);
	s2.insert(50);

	for (set<int, MyCompare>::iterator it = s2.begin(); it != s2.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

int main() {

	test01();

	system("pause");

	return 0;
}

概要: ファンクターを使用してセット コンテナーの並べ替え規則を指定する

 

例 2セットはカスタム データ型を格納します

#include <set>
#include <string>

class Person
{
public:
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}

	string m_Name;
	int m_Age;

};
class comparePerson
{
public:
	bool operator()(const Person& p1, const Person &p2)
	{
		//按照年龄进行排序  降序
		return p1.m_Age > p2.m_Age;
	}
};

void test01()
{
	set<Person, comparePerson> s;

	Person p1("刘备", 23);
	Person p2("关羽", 27);
	Person p3("张飞", 25);
	Person p4("赵云", 21);

	s.insert(p1);
	s.insert(p2);
	s.insert(p3);
	s.insert(p4);

	for (set<Person, comparePerson>::iterator it = s.begin(); it != s.end(); it++)
	{
		cout << "姓名: " << it->m_Name << " 年龄: " << it->m_Age << endl;
	}
}
int main() {

	test01();

	system("pause");

	return 0;
}

要約:

カスタム データ型の場合、データを挿入するには照合順序を指定する必要があります。

 

3.9 マップ/マルチマップコンテナ

3.9.1 マップの基本概念

序章:

  • マップ内のすべての要素はペアです

  • ペアの最初の要素はインデックスとして機能するキー (キー値) で、2 番目の要素は値 (実際の値) です。

  • すべての要素は、要素のキー値に従って自動的にソートされます。

自然:

  • map/multimap は連想コンテナであり、基礎となる構造はバイナリ ツリーで実装されます。

アドバンテージ:

  • キー値に従って値をすぐに見つけることができます

マップとマルチマップの違い:

  • マップはコンテナ内で重複するキー値要素を許可しません

  • マルチマップではコンテナ内で重複したキー値要素が許可されます

3.9.2 マップの構築と割り当て

機能の説明:

  • 値を構築してマップコンテナに割り当てる

関数プロトタイプ:

構造:

  • map<T1, T2> mp;//マップのデフォルトのコンストラクター:

  • map(const map &mp);// コンストラクターをコピー

割り当て:

  • map& operator=(const map &mp);// 等号演算子をオーバーロードします

例:

#include <map>

void printMap(map<int,int>&m)
{
	for (map<int, int>::iterator it = m.begin(); it != m.end(); it++)
	{
		cout << "key = " << it->first << " value = " << it->second << endl;
	}
	cout << endl;
}

void test01()
{
	map<int,int>m; //默认构造
	m.insert(pair<int, int>(1, 10));
	m.insert(pair<int, int>(2, 20));
	m.insert(pair<int, int>(3, 30));
	printMap(m);

	map<int, int>m2(m); //拷贝构造
	printMap(m2);

	map<int, int>m3;
	m3 = m2; //赋值
	printMap(m3);
}

int main() {

	test01();

	system("pause");

	return 0;
}

概要: マップ内のすべての要素はペアで表示され、データを挿入するときにペアが使用されます。

 

3.9.3 マップのサイズとスワップ

機能の説明:

  • 統計マップ コンテナのサイズと交換マップ コンテナ

関数プロトタイプ:

  • size();// コンテナ内の要素の数を返します

  • empty();// コンテナが空かどうかを確認する

  • swap(st);// 2 つのコレクション コンテナを交換します

例:

#include <map>

void printMap(map<int,int>&m)
{
	for (map<int, int>::iterator it = m.begin(); it != m.end(); it++)
	{
		cout << "key = " << it->first << " value = " << it->second << endl;
	}
	cout << endl;
}

void test01()
{
	map<int, int>m;
	m.insert(pair<int, int>(1, 10));
	m.insert(pair<int, int>(2, 20));
	m.insert(pair<int, int>(3, 30));

	if (m.empty())
	{
		cout << "m为空" << endl;
	}
	else
	{
		cout << "m不为空" << endl;
		cout << "m的大小为: " << m.size() << endl;
	}
}


//交换
void test02()
{
	map<int, int>m;
	m.insert(pair<int, int>(1, 10));
	m.insert(pair<int, int>(2, 20));
	m.insert(pair<int, int>(3, 30));

	map<int, int>m2;
	m2.insert(pair<int, int>(4, 100));
	m2.insert(pair<int, int>(5, 200));
	m2.insert(pair<int, int>(6, 300));

	cout << "交换前" << endl;
	printMap(m);
	printMap(m2);

	cout << "交换后" << endl;
	m.swap(m2);
	printMap(m);
	printMap(m2);
}

int main() {

	test01();

	test02();

	system("pause");

	return 0;
}

要約:

  • 統計サイズ --- サイズ

  • 空かどうかを判断します --- 空です

  • コンテナを交換 --- スワップ

3.9.4 マップの挿入と削除

機能の説明:

  • マップ コンテナはデータの挿入とデータの削除を行います。

関数プロトタイプ:

  • insert(elem);// 要素をコンテナに挿入します。

  • clear();//すべての要素をクリア

  • erase(pos);// pos イテレータが指す要素を削除し、次の要素のイテレータを返します。

  • erase(beg, end);//区間 [beg, end) 内のすべての要素を削除し、次の要素のイテレータを返します。

  • erase(key);//コンテナ内の値がkeyである要素を削除します。

例:

#include <map>

void printMap(map<int,int>&m)
{
	for (map<int, int>::iterator it = m.begin(); it != m.end(); it++)
	{
		cout << "key = " << it->first << " value = " << it->second << endl;
	}
	cout << endl;
}

void test01()
{
	//插入
	map<int, int> m;
	//第一种插入方式
	m.insert(pair<int, int>(1, 10));
	//第二种插入方式
	m.insert(make_pair(2, 20));
	//第三种插入方式
	m.insert(map<int, int>::value_type(3, 30));
	//第四种插入方式
	m[4] = 40; 
	printMap(m);

	//删除
	m.erase(m.begin());
	printMap(m);

	m.erase(3);
	printMap(m);

	//清空
	m.erase(m.begin(),m.end());
	m.clear();
	printMap(m);
}

int main() {

	test01();

	system("pause");

	return 0;
}

要約:

  • マップを挿入する方法はたくさんありますが、そのうちの 1 つを覚えておいてください。

  • 挿入 --- 挿入

  • 削除 --- 消去

  • 空 --- クリア

3.9.5 地図の検索と統計

機能の説明:

  • マップコンテナ上のデータと統計を検索する

関数プロトタイプ:

  • find(key);// キーが存在するかどうかを調べ、存在する場合はキーの要素のイテレータを返し、存在しない場合は set.end() を返します。

  • count(key);// キー内の要素の数をカウントします。

例:

#include <map>

//查找和统计
void test01()
{
	map<int, int>m; 
	m.insert(pair<int, int>(1, 10));
	m.insert(pair<int, int>(2, 20));
	m.insert(pair<int, int>(3, 30));

	//查找
	map<int, int>::iterator pos = m.find(3);

	if (pos != m.end())
	{
		cout << "找到了元素 key = " << (*pos).first << " value = " << (*pos).second << endl;
	}
	else
	{
		cout << "未找到元素" << endl;
	}

	//统计
	int num = m.count(3);
	cout << "num = " << num << endl;
}

int main() {

	test01();

	system("pause");

	return 0;
}

要約:

  • Find --- find (反復子を返す)

  • 統計 --- カウント (マップの場合、結果は 0 または 1)

 

3.9.6 マップコンテナのソート

学習目標:

  • マップコンテナのデフォルトのソートルールは、キー値に従って小さいものから大きいものへとソートされます。ソートルールを変更する方法をマスターしてください。

主な技術的なポイント:

  • ファンクターを使用すると、並べ替えルールを変更できます

例:

#include <map>

class MyCompare {
public:
	bool operator()(int v1, int v2) {
		return v1 > v2;
	}
};

void test01() 
{
	//默认从小到大排序
	//利用仿函数实现从大到小排序
	map<int, int, MyCompare> m;

	m.insert(make_pair(1, 10));
	m.insert(make_pair(2, 20));
	m.insert(make_pair(3, 30));
	m.insert(make_pair(4, 40));
	m.insert(make_pair(5, 50));

	for (map<int, int, MyCompare>::iterator it = m.begin(); it != m.end(); it++) {
		cout << "key:" << it->first << " value:" << it->second << endl;
	}
}
int main() {

	test01();

	system("pause");

	return 0;
}

要約:

  • ファンクターを使用してマップ コンテナーの並べ替え規則を指定します

  • カスタム データ型の場合、マップではセット コンテナーと同様に並べ替えルールを指定する必要があります。

3.10 ケース - 従業員のグループ化

3.10.1 ケースの説明

  • 会社は本日 10 人の従業員 (ABCDEFGHIJ) を採用しました。10 人の従業員が入社したら、その部門で働くように割り当てられる必要があります

  • 社員情報は氏名、給与、部署は企画、美術、研究開発に分かれています。

  • 10 人の従業員に部門と給与をランダムに割り当てます

  • マルチマップキー(部門番号)値(従業員)による情報の挿入

  • 従業員情報を部門ごとに表示

3.10.2 実装手順

  1. 10 人の従業員を作成し、ベクターに配置します。

  2. ベクター コンテナを走査し、各従業員を取り出し、ランダムなグループ化を実行します。

  3. グループ化後、従業員の部門番号をキーとして、特定の従業員を値として使用し、それらをマルチマップ コンテナーに入れます。

  4. 従業員情報を部門ごとに表示

ケースコード:

#include<iostream>
using namespace std;
#include <vector>
#include <string>
#include <map>
#include <ctime>

/*
- 公司今天招聘了10个员工(ABCDEFGHIJ),10名员工进入公司之后,需要指派员工在那个部门工作
- 员工信息有: 姓名  工资组成;部门分为:策划、美术、研发
- 随机给10名员工分配部门和工资
- 通过multimap进行信息的插入  key(部门编号) value(员工)
- 分部门显示员工信息
*/

#define CEHUA  0
#define MEISHU 1
#define YANFA  2

class Worker
{
public:
	string m_Name;
	int m_Salary;
};

void createWorker(vector<Worker>&v)
{
	string nameSeed = "ABCDEFGHIJ";
	for (int i = 0; i < 10; i++)
	{
		Worker worker;
		worker.m_Name = "员工";
		worker.m_Name += nameSeed[i];

		worker.m_Salary = rand() % 10000 + 10000; // 10000 ~ 19999
		//将员工放入到容器中
		v.push_back(worker);
	}
}

//员工分组
void setGroup(vector<Worker>&v,multimap<int,Worker>&m)
{
	for (vector<Worker>::iterator it = v.begin(); it != v.end(); it++)
	{
		//产生随机部门编号
		int deptId = rand() % 3; // 0 1 2 

		//将员工插入到分组中
		//key部门编号,value具体员工
		m.insert(make_pair(deptId, *it));
	}
}

void showWorkerByGourp(multimap<int,Worker>&m)
{
	// 0  A  B  C   1  D  E   2  F G ...
	cout << "策划部门:" << endl;

	multimap<int,Worker>::iterator pos = m.find(CEHUA);
	int count = m.count(CEHUA); // 统计具体人数
	int index = 0;
	for (; pos != m.end() && index < count; pos++ , index++)
	{
		cout << "姓名: " << pos->second.m_Name << " 工资: " << pos->second.m_Salary << endl;
	}

	cout << "----------------------" << endl;
	cout << "美术部门: " << endl;
	pos = m.find(MEISHU);
	count = m.count(MEISHU); // 统计具体人数
	index = 0;
	for (; pos != m.end() && index < count; pos++, index++)
	{
		cout << "姓名: " << pos->second.m_Name << " 工资: " << pos->second.m_Salary << endl;
	}

	cout << "----------------------" << endl;
	cout << "研发部门: " << endl;
	pos = m.find(YANFA);
	count = m.count(YANFA); // 统计具体人数
	index = 0;
	for (; pos != m.end() && index < count; pos++, index++)
	{
		cout << "姓名: " << pos->second.m_Name << " 工资: " << pos->second.m_Salary << endl;
	}

}

int main() {

	srand((unsigned int)time(NULL));

	//1、创建员工
	vector<Worker>vWorker;
	createWorker(vWorker);

	//2、员工分组
	multimap<int, Worker>mWorker;
	setGroup(vWorker, mWorker);


	//3、分组显示员工
	showWorkerByGourp(mWorker);

	测试
	//for (vector<Worker>::iterator it = vWorker.begin(); it != vWorker.end(); it++)
	//{
	//	cout << "姓名: " << it->m_Name << " 工资: " << it->m_Salary << endl;
	//}

	system("pause");

	return 0;
}

要約:

  • データがキーと値のペアの形式で存在する場合は、マップまたはマルチマップの使用を検討できます。

 

4 STL 関数オブジェクト

4.1 関数オブジェクト

4.1.1 関数オブジェクトの概念

コンセプト:

  • 関数呼び出し演算子をオーバーロードするクラス。そのオブジェクトは関数オブジェクトと呼ばれることがよくあります。

  • 関数オブジェクトがオーバーロード () を使用すると、ファンクターとも呼ばれる関数呼び出しのように動作します。

自然:

関数オブジェクト (ファンクター) は関数ではなくクラスです

4.1.2 関数オブジェクトの使用法

特徴:

  • 関数オブジェクトを使用すると、通常の関数と同じように呼び出すことができ、パラメータを指定したり、戻り値を指定したりできます。

  • 関数オブジェクトは通常の関数の概念を超え、関数オブジェクトは独自の状態を持つことができます

  • 関数オブジェクトは引数として渡すことができます

例:

#include <string>
​
//1、函数对象在使用时,可以像普通函数那样调用, 可以有参数,可以有返回值
class MyAdd
{
public :
    int operator()(int v1,int v2)
    {
        return v1 + v2;
    }
};
​
void test01()
{
    MyAdd myAdd;
    cout << myAdd(10, 10) << endl;
}
​
//2、函数对象可以有自己的状态
class MyPrint
{
public:
    MyPrint()
    {
        count = 0;
    }
    void operator()(string test)
    {
        cout << test << endl;
        count++; //统计使用次数
    }
​
    int count; //内部自己的状态
};
void test02()
{
    MyPrint myPrint;
    myPrint("hello world");
    myPrint("hello world");
    myPrint("hello world");
    cout << "myPrint调用次数为: " << myPrint.count << endl;
}
​
//3、函数对象可以作为参数传递
void doPrint(MyPrint &mp , string test)
{
    mp(test);
}
​
void test03()
{
    MyPrint myPrint;
    doPrint(myPrint, "Hello C++");
}
​
int main() {
​
    //test01();
    //test02();
    test03();
​
    system("pause");
​
    return 0;
}

要約:

  • ファンクターは非常に柔軟であり、パラメーターとして渡すことができます。

4.2 述語

4.2.1 述語の概念

コンセプト:

  • bool 型を返すファンクタは述語と呼ばれます

  • Operator() が 1 つの引数を取る場合、それは単項述語と呼ばれます

  • Operator() が 2 つの引数を取る場合、それはバイナリ述語と呼ばれます

4.2.2 単項述語

例:

#include <vector>
#include <algorithm>
​
//1.一元谓词
struct GreaterFive{
    bool operator()(int val) {
        return val > 5;
    }
};
​
void test01() {
​
    vector<int> v;
    for (int i = 0; i < 10; i++)
    {
        v.push_back(i);
    }
​
    vector<int>::iterator it = find_if(v.begin(), v.end(), GreaterFive());
    if (it == v.end()) {
        cout << "没找到!" << endl;
    }
    else {
        cout << "找到:" << *it << endl;
    }
​
}
​
int main() {
​
    test01();
​
    system("pause");
​
    return 0;
}

概要: パラメーターが 1 つだけある述語は、単項述語と呼ばれます。

4.2.3 バイナリ述語

例:

#include <vector>
#include <algorithm>
//二元谓词
class MyCompare
{
public:
    bool operator()(int num1, int num2)
    {
        return num1 > num2;
    }
};
​
void test01()
{
    vector<int> v;
    v.push_back(10);
    v.push_back(40);
    v.push_back(20);
    v.push_back(30);
    v.push_back(50);
​
    //默认从小到大
    sort(v.begin(), v.end());
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
    cout << "----------------------------" << endl;
​
    //使用函数对象改变算法策略,排序从大到小
    sort(v.begin(), v.end(), MyCompare());
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}
​
int main() {
​
    test01();
​
    system("pause");
​
    return 0;
}

概要: パラメータが 2 つだけの述語はバイナリ述語と呼ばれます

4.3 組み込み関数オブジェクト

4.3.1 組み込み関数オブジェクトの意味

コンセプト:

  • STL にはいくつかの組み込み関数オブジェクトがあります

分類:

  • 算術関数

  • 関係関数

  • 論理関数

使用法:

  • これらのファンクターによって生成されたオブジェクトは、通常の関数とまったく同じ方法で使用されます。

  • 組み込み関数オブジェクトを使用するには、ヘッダー ファイルをインポートする必要があります#include<functional>

4.3.2 算術関手

機能の説明:

  • 4つの操作を実現

  • このうち、negate は単項演算、その他は 2 項演算です。

ファンクターのプロトタイプ:

  • template<class T> T plus<T>// 加算関数

  • template<class T> T minus<T>//減算関数

  • template<class T> T multiplies<T>// 乗算関数

  • template<class T> T divides<T>// 除算関数

  • template<class T> T modulus<T>//模倣関数を取得します

  • template<class T> T negate<T>// 逆関数を取得します

例:

#include <functional>
//negate
void test01()
{
    negate<int> n;
    cout << n(50) << endl;
}
​
//plus
void test02()
{
    plus<int> p;
    cout << p(10, 20) << endl;
}
​
int main() {
​
    test01();
    test02();
​
    system("pause");
​
    return 0;
}

概要: 組み込み関数オブジェクトを使用する場合、ヘッダー ファイルを導入する必要があります#include <functional>

4.3.3 関係関数

機能の説明:

  • 関係比較を実現する

ファンクターのプロトタイプ:

  • template<class T> bool equal_to<T>//同等

  • template<class T> bool not_equal_to<T>//等しくない

  • template<class T> bool greater<T>// 以上

  • template<class T> bool greater_equal<T>//以上

  • template<class T> bool less<T>//未満

  • template<class T> bool less_equal<T>// 以下

例:

#include <functional>
#include <vector>
#include <algorithm>
​
class MyCompare
{
public:
    bool operator()(int v1,int v2)
    {
        return v1 > v2;
    }
};
void test01()
{
    vector<int> v;
​
    v.push_back(10);
    v.push_back(30);
    v.push_back(50);
    v.push_back(40);
    v.push_back(20);
​
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;
​
    //自己实现仿函数
    //sort(v.begin(), v.end(), MyCompare());
    //STL内建仿函数  大于仿函数
    sort(v.begin(), v.end(), greater<int>());
​
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;
}
​
int main() {
​
    test01();
​
    system("pause");
​
    return 0;
}

概要: 最も一般的に使用されるリレーショナル関手は、「より大きい<> より大きい」です。

4.3.4 論理ファンクター

機能の説明:

  • 論理演算の実装

関数プロトタイプ:

  • template<class T> bool logical_and<T>// 論理積

  • template<class T> bool logical_or<T>// 論理和

  • template<class T> bool logical_not<T>// 論理否定

例:

#include <vector>
#include <functional>
#include <algorithm>
void test01()
{
    vector<bool> v;
    v.push_back(true);
    v.push_back(false);
    v.push_back(true);
    v.push_back(false);
​
    for (vector<bool>::iterator it = v.begin();it!= v.end();it++)
    {
        cout << *it << " ";
    }
    cout << endl;
​
    //逻辑非  将v容器搬运到v2中,并执行逻辑非运算
    vector<bool> v2;
    v2.resize(v.size());
    transform(v.begin(), v.end(),  v2.begin(), logical_not<bool>());
    for (vector<bool>::iterator it = v2.begin(); it != v2.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}
​
int main() {
​
    test01();
​
    system("pause");
​
    return 0;
}

要約: 論理関手の実際的な応用例はほとんどありません。理解するだけで十分です。

 

 

5 つの STL 共通アルゴリズム

概要

  • アルゴリズムは主にヘッダー ファイルで構成されます<algorithm> <functional> <numeric>

  • <algorithm>これはすべての STL ヘッダー ファイルの中で最大であり、その範囲には比較、交換、検索、走査操作、コピー、変更などが含まれます。

  • <numeric>サイズが小さく、シーケンスに対して単純な数学演算を実行するためのいくつかのテンプレート関数のみが含まれています

  • <functional>一部のテンプレート クラスは、関数オブジェクトを宣言するために定義されています。

5.1 一般的に使用されるトラバーサル アルゴリズム

学習目標:

  • 一般的な走査アルゴリズムをマスターする

アルゴリズムの紹介:

  • for_each// コンテナを走査する

  • transform// コンテナを別のコンテナに移動します

5.1.1 for_each

機能の説明:

  • トラバーサルコンテナを実装する

関数プロトタイプ:

  • for_each(iterator beg, iterator end, _func);

    // 走査アルゴリズムはコンテナ要素を走査します

    // beg はイテレータを開始します

    // 終了反復子

    // _func 関数または関数オブジェクト

例:

#include <algorithm>
#include <vector>
​
//普通函数
void print01(int val) 
{
    cout << val << " ";
}
//函数对象
class print02 
{
 public:
    void operator()(int val) 
    {
        cout << val << " ";
    }
};
​
//for_each算法基本用法
void test01() {
​
    vector<int> v;
    for (int i = 0; i < 10; i++) 
    {
        v.push_back(i);
    }
​
    //遍历算法
    for_each(v.begin(), v.end(), print01);
    cout << endl;
​
    for_each(v.begin(), v.end(), print02());
    cout << endl;
}
​
int main() {
​
    test01();
​
    system("pause");
​
    return 0;
}

概要: for_each は実際の開発で最も一般的に使用されるトラバーサル アルゴリズムであり、習得する必要があります。

5.1.2 変換

機能の説明:

  • コンテナを別のコンテナに移動する

関数プロトタイプ:

  • transform(iterator beg1, iterator end1, iterator beg2, _func);

//beg1 ソースコンテナ開始イテレータ

//end1 ソースコンテナの終了イテレータ

//beg2 ターゲットコンテナ開始イテレータ

//_func 関数または関数オブジェクト

例:

#include<vector>
#include<algorithm>
​
//常用遍历算法  搬运 transform
​
class TransForm
{
public:
    int operator()(int val)
    {
        return val;
    }
​
};
​
class MyPrint
{
public:
    void operator()(int val)
    {
        cout << val << " ";
    }
};
​
void test01()
{
    vector<int>v;
    for (int i = 0; i < 10; i++)
    {
        v.push_back(i);
    }
​
    vector<int>vTarget; //目标容器
​
    vTarget.resize(v.size()); // 目标容器需要提前开辟空间
​
    transform(v.begin(), v.end(), vTarget.begin(), TransForm());
​
    for_each(vTarget.begin(), vTarget.end(), MyPrint());
}
​
int main() {
​
    test01();
​
    system("pause");
​
    return 0;
}

概要:輸送対象のコンテナはあらかじめスペースを空けておかなければ正常に輸送できません

5.2 一般的な検索アルゴリズム

学習目標:

  • 一般的な検索アルゴリズムをマスターする

アルゴリズムの紹介:

  • find//要素を検索

  • find_if// 条件で要素を検索

  • adjacent_find// 隣接する重複要素を検索します

  • binary_search// 二分探索法

  • count// 要素の数をカウントします

  • count_if// 条件ごとに要素数をカウントする

5.2.1 検索

機能の説明:

  • 指定された要素を検索し、指定された要素を返すイテレータを検索します。見つからない場合は、終了イテレータを返します。 end()

関数プロトタイプ:

  • find(iterator beg, iterator end, value);

    // 要素を値で検索し、見つかった場合は指定された位置のイテレータを返し、見つからない場合は終了イテレータの位置を返します。

    // beg はイテレータを開始します

    // 終了反復子

    // value が探している要素

例:

#include <algorithm>
#include <vector>
#include <string>
void test01() {
​
    vector<int> v;
    for (int i = 0; i < 10; i++) {
        v.push_back(i + 1);
    }
    //查找容器中是否有 5 这个元素
    vector<int>::iterator it = find(v.begin(), v.end(), 5);
    if (it == v.end()) 
    {
        cout << "没有找到!" << endl;
    }
    else 
    {
        cout << "找到:" << *it << endl;
    }
}
​
class Person {
public:
    Person(string name, int age) 
    {
        this->m_Name = name;
        this->m_Age = age;
    }
    //重载==
    bool operator==(const Person& p) 
    {
        if (this->m_Name == p.m_Name && this->m_Age == p.m_Age) 
        {
            return true;
        }
        return false;
    }
​
public:
    string m_Name;
    int m_Age;
};
​
void test02() {
​
    vector<Person> v;
​
    //创建数据
    Person p1("aaa", 10);
    Person p2("bbb", 20);
    Person p3("ccc", 30);
    Person p4("ddd", 40);
​
    v.push_back(p1);
    v.push_back(p2);
    v.push_back(p3);
    v.push_back(p4);
​
    vector<Person>::iterator it = find(v.begin(), v.end(), p2);
    if (it == v.end()) 
    {
        cout << "没有找到!" << endl;
    }
    else 
    {
        cout << "找到姓名:" << it->m_Name << " 年龄: " << it->m_Age << endl;
    }
}

概要: find を使用してコンテナ内の指定された要素を検索します。戻り値はイテレータです。

5.2.2 find_if

機能の説明:

  • 条件による要素の検索

関数プロトタイプ:

  • find_if(iterator beg, iterator end, _Pred);

    // 要素を値で検索し、見つかった場合は指定された位置のイテレータを返し、見つからない場合は終了イテレータの位置を返します。

    // beg はイテレータを開始します

    // 終了反復子

    // _Pred 関数または述語 (bool 型を返す関数関数)

例:

#include <algorithm>
#include <vector>
#include <string>
​
//内置数据类型
class GreaterFive
{
public:
    bool operator()(int val)
    {
        return val > 5;
    }
};
​
void test01() {
​
    vector<int> v;
    for (int i = 0; i < 10; i++) {
        v.push_back(i + 1);
    }
​
    vector<int>::iterator it = find_if(v.begin(), v.end(), GreaterFive());
    if (it == v.end()) {
        cout << "没有找到!" << endl;
    }
    else {
        cout << "找到大于5的数字:" << *it << endl;
    }
}
​
//自定义数据类型
class Person {
public:
    Person(string name, int age)
    {
        this->m_Name = name;
        this->m_Age = age;
    }
public:
    string m_Name;
    int m_Age;
};
​
class Greater20
{
public:
    bool operator()(Person &p)
    {
        return p.m_Age > 20;
    }
​
};
​
void test02() {
​
    vector<Person> v;
​
    //创建数据
    Person p1("aaa", 10);
    Person p2("bbb", 20);
    Person p3("ccc", 30);
    Person p4("ddd", 40);
​
    v.push_back(p1);
    v.push_back(p2);
    v.push_back(p3);
    v.push_back(p4);
​
    vector<Person>::iterator it = find_if(v.begin(), v.end(), Greater20());
    if (it == v.end())
    {
        cout << "没有找到!" << endl;
    }
    else
    {
        cout << "找到姓名:" << it->m_Name << " 年龄: " << it->m_Age << endl;
    }
}
​
int main() {
​
    //test01();
​
    test02();
​
    system("pause");
​
    return 0;
}

概要: find_if は条件によって検索して検索をより柔軟にし、提供されたファンクターはさまざまな戦略を変更できます。

5.2.3 隣接検索

機能の説明:

  • 隣接する重複要素を検索する

関数プロトタイプ:

  • adjacent_find(iterator beg, iterator end);

    // 隣接する繰り返し要素を検索し、隣接する要素の最初の位置にイテレータを返します。

    // beg はイテレータを開始します

    // 終了反復子

例:

#include <algorithm>
#include <vector>
​
void test01()
{
    vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(5);
    v.push_back(2);
    v.push_back(4);
    v.push_back(4);
    v.push_back(3);
​
    //查找相邻重复元素
    vector<int>::iterator it = adjacent_find(v.begin(), v.end());
    if (it == v.end()) {
        cout << "找不到!" << endl;
    }
    else {
        cout << "找到相邻重复元素为:" << *it << endl;
    }
}

概要: 面接の質問で隣接して繰り返される要素を見つけた場合は、STL の隣接検索アルゴリズムを使用することを忘れないでください。

機能の説明:

  • 指定された要素が存在するかどうかを調べる

関数プロトタイプ:

  • bool binary_search(iterator beg, iterator end, value);

    // 指定された要素を検索し、見つかった場合は true を返し、それ以外の場合は false を返します。

    // 注:順序付けされていないシーケンスでは使用できません

    // beg はイテレータを開始します

    // 終了反復子

    // value が探している要素

例:

#include <algorithm>
#include <vector>
​
void test01()
{
    vector<int>v;
​
    for (int i = 0; i < 10; i++)
    {
        v.push_back(i);
    }
    //二分查找
    bool ret = binary_search(v.begin(), v.end(),2);
    if (ret)
    {
        cout << "找到了" << endl;
    }
    else
    {
        cout << "未找到" << endl;
    }
}
​
int main() {
​
    test01();
​
    system("pause");
​
    return 0;
}

要約:二分探索法は非常に効率的ですが、検索されたコンテナ内の要素には順序付けされたシーケンスが必要であることに注意してください。

5.2.5カウント

機能の説明:

  • 統計要素の数

関数プロトタイプ:

  • count(iterator beg, iterator end, value);

    // 要素の出現数をカウントします

    // beg はイテレータを開始します

    // 終了反復子

    // 値統計の要素

例:

#include <algorithm>
#include <vector>
​
//内置数据类型
void test01()
{
    vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(4);
    v.push_back(5);
    v.push_back(3);
    v.push_back(4);
    v.push_back(4);
​
    int num = count(v.begin(), v.end(), 4);
​
    cout << "4的个数为: " << num << endl;
}
​
//自定义数据类型
class Person
{
public:
    Person(string name, int age)
    {
        this->m_Name = name;
        this->m_Age = age;
    }
    bool operator==(const Person & p)
    {
        if (this->m_Age == p.m_Age)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    string m_Name;
    int m_Age;
};
​
void test02()
{
    vector<Person> v;
​
    Person p1("刘备", 35);
    Person p2("关羽", 35);
    Person p3("张飞", 35);
    Person p4("赵云", 30);
    Person p5("曹操", 25);
​
    v.push_back(p1);
    v.push_back(p2);
    v.push_back(p3);
    v.push_back(p4);
    v.push_back(p5);
    
    Person p("诸葛亮",35);
​
    int num = count(v.begin(), v.end(), p);
    cout << "num = " << num << endl;
}
int main() {
​
    //test01();
​
    test02();
​
    system("pause");
​
    return 0;
}

概要:カスタム データ型をカウントするときは、オーバーロードと連携する必要がありますoperator==

5.2.6 count_if

機能の説明:

  • 条件ごとに要素数をカウントする

関数プロトタイプ:

  • count_if(iterator beg, iterator end, _Pred);

    // 条件ごとに要素の出現数をカウントします

    // beg はイテレータを開始します

    // 終了反復子

    // _Pred述語

例:

#include <algorithm>
#include <vector>
​
class Greater4
{
public:
    bool operator()(int val)
    {
        return val >= 4;
    }
};
​
//内置数据类型
void test01()
{
    vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(4);
    v.push_back(5);
    v.push_back(3);
    v.push_back(4);
    v.push_back(4);
​
    int num = count_if(v.begin(), v.end(), Greater4());
​
    cout << "大于4的个数为: " << num << endl;
}
​
//自定义数据类型
class Person
{
public:
    Person(string name, int age)
    {
        this->m_Name = name;
        this->m_Age = age;
    }
​
    string m_Name;
    int m_Age;
};
​
class AgeLess35
{
public:
    bool operator()(const Person &p)
    {
        return p.m_Age < 35;
    }
};
void test02()
{
    vector<Person> v;
​
    Person p1("刘备", 35);
    Person p2("关羽", 35);
    Person p3("张飞", 35);
    Person p4("赵云", 30);
    Person p5("曹操", 25);
​
    v.push_back(p1);
    v.push_back(p2);
    v.push_back(p3);
    v.push_back(p4);
    v.push_back(p5);
​
    int num = count_if(v.begin(), v.end(), AgeLess35());
    cout << "小于35岁的个数:" << num << endl;
}
​
​
int main() {
​
    //test01();
​
    test02();
​
    system("pause");
​
    return 0;
}

概要:値によるカウントと条件による count_if

5.3 一般的な並べ替えアルゴリズム

学習目標:

  • 一般的な並べ替えアルゴリズムをマスターする

アルゴリズムの紹介:

  • sort//コンテナ内の要素を並べ替える

  • random_shuffle//指定された範囲内の要素をシャッフルして順序をランダムに調整します

  • merge// コンテナ要素はマージされ、別のコンテナに保存されます

  • reverse// 指定された範囲の要素を反転します

5.3.1 並べ替え

機能の説明:

  • コンテナ内の要素を並べ替える

関数プロトタイプ:

  • sort(iterator beg, iterator end, _Pred);

    // 要素を値で検索し、見つかった場合は指定された位置のイテレータを返し、見つからない場合は終了イテレータの位置を返します。

    // beg はイテレータを開始します

    // 終了反復子

    // _Pred述語

例:

#include <algorithm>
#include <vector>
​
void myPrint(int val)
{
    cout << val << " ";
}
​
void test01() {
    vector<int> v;
    v.push_back(10);
    v.push_back(30);
    v.push_back(50);
    v.push_back(20);
    v.push_back(40);
​
    //sort默认从小到大排序
    sort(v.begin(), v.end());
    for_each(v.begin(), v.end(), myPrint);
    cout << endl;
​
    //从大到小排序
    sort(v.begin(), v.end(), greater<int>());
    for_each(v.begin(), v.end(), myPrint);
    cout << endl;
}
​
int main() {
​
    test01();
​
    system("pause");
​
    return 0;
}

要約:ソートは開発において最も一般的に使用されるアルゴリズムの 1 つであり、習得する必要があります。

5.3.2 ランダムシャッフル

機能の説明:

  • 指定した範囲の要素をシャッフルして順序をランダムに調整します

関数プロトタイプ:

  • random_shuffle(iterator beg, iterator end);

    // 指定された範囲内の要素の順序をランダムに調整します

    // beg はイテレータを開始します

    // 終了反復子

例:

#include <algorithm>
#include <vector>
#include <ctime>
​
class myPrint
{
public:
    void operator()(int val)
    {
        cout << val << " ";
    }
};
​
void test01()
{
    srand((unsigned int)time(NULL));
    vector<int> v;
    for(int i = 0 ; i < 10;i++)
    {
        v.push_back(i);
    }
    for_each(v.begin(), v.end(), myPrint());
    cout << endl;
​
    //打乱顺序
    random_shuffle(v.begin(), v.end());
    for_each(v.begin(), v.end(), myPrint());
    cout << endl;
}
​
int main() {
​
    test01();
​
    system("pause");
​
    return 0;
}

概要: randan_shuffle シャッフル アルゴリズムはより実用的です。使用する場合は乱数シードを追加することを忘れないでください。

5.3.3 マージ

機能の説明:

  • 2 つのコンテナ要素がマージされ、別のコンテナに格納されます。

関数プロトタイプ:

  • merge(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);

    // コンテナ要素はマージされ、別のコンテナに保存されます

    // 注: 2 つのコンテナは順番に並んでいる必要があります

    // beg1 コンテナ 1 開始イテレータ // end1 コンテナ 1 終了イテレータ // beg2 コンテナ 2 開始イテレータ // end2 コンテナ 2 終了イテレータ // dest 宛先コンテナ開始イテレータ

例:

#include <algorithm>
#include <vector>
​
class myPrint
{
public:
    void operator()(int val)
    {
        cout << val << " ";
    }
};
​
void test01()
{
    vector<int> v1;
    vector<int> v2;
    for (int i = 0; i < 10 ; i++) 
    {
        v1.push_back(i);
        v2.push_back(i + 1);
    }
​
    vector<int> vtarget;
    //目标容器需要提前开辟空间
    vtarget.resize(v1.size() + v2.size());
    //合并  需要两个有序序列
    merge(v1.begin(), v1.end(), v2.begin(), v2.end(), vtarget.begin());
    for_each(vtarget.begin(), vtarget.end(), myPrint());
    cout << endl;
}
​
int main() {
​
    test01();
​
    system("pause");
​
    return 0;
}

概要:マージによってマージされた 2 つのコンテナの順序付けされたシーケンス

5.3.4 リバース

機能の説明:

  • コンテナ内の要素を反転します

関数プロトタイプ:

  • reverse(iterator beg, iterator end);

    // 指定された範囲の要素を反転します

    // beg はイテレータを開始します

    // 終了反復子

例:

#include <algorithm>
#include <vector>
​
class myPrint
{
public:
    void operator()(int val)
    {
        cout << val << " ";
    }
};
​
void test01()
{
    vector<int> v;
    v.push_back(10);
    v.push_back(30);
    v.push_back(50);
    v.push_back(20);
    v.push_back(40);
​
    cout << "反转前: " << endl;
    for_each(v.begin(), v.end(), myPrint());
    cout << endl;
​
    cout << "反转后: " << endl;
​
    reverse(v.begin(), v.end());
    for_each(v.begin(), v.end(), myPrint());
    cout << endl;
}
​
int main() {
​
    test01();
​
    system("pause");
​
    return 0;
}

概要: reverse は、間隔内の要素を逆にします。面接の質問には以下のような内容が含まれる場合があります。

5.4 一般的なコピーおよび置換アルゴリズム

学習目標:

  • 一般的なコピーおよび置換アルゴリズムをマスターする

アルゴリズムの紹介:

  • copy// コンテナ内の指定範囲の要素を別のコンテナにコピーします

  • replace// コンテナ内の指定された範囲内の古い要素を新しい要素に変更します

  • replace_if// コンテナ内の指定範囲内の条件を満たす要素を新しい要素に置き換えます

  • swap// 2 つのコンテナの要素を交換します

5.4.1 コピー

機能の説明:

  • コンテナ内の指定された範囲の要素を別のコンテナにコピーします

関数プロトタイプ:

  • copy(iterator beg, iterator end, iterator dest);

    // 要素を値で検索し、見つかった場合は指定された位置のイテレータを返し、見つからない場合は終了イテレータの位置を返します。

    // beg はイテレータを開始します

    // 終了反復子

    // dest ターゲットの開始イテレータ

例:

#include <algorithm>
#include <vector>
​
class myPrint
{
public:
    void operator()(int val)
    {
        cout << val << " ";
    }
};
​
void test01()
{
    vector<int> v1;
    for (int i = 0; i < 10; i++) {
        v1.push_back(i + 1);
    }
    vector<int> v2;
    v2.resize(v1.size());
    copy(v1.begin(), v1.end(), v2.begin());
​
    for_each(v2.begin(), v2.end(), myPrint());
    cout << endl;
}
​
int main() {
​
    test01();
​
    system("pause");
​
    return 0;
}

概要:コピー アルゴリズムを使用してコピーする場合、ターゲット コンテナーは事前にスペースを空けておくことを記憶します。

5.4.2 置換

機能の説明:

  • コンテナ内の指定された範囲内の古い要素を新しい要素に変更します

関数プロトタイプ:

  • replace(iterator beg, iterator end, oldvalue, newvalue);

    // 間隔内の古い要素を新しい要素に置き換えます

    // beg はイテレータを開始します

    // 終了反復子

    // oldvalue 古い要素

    // newvalue 新しい要素

例:

#include <algorithm>
#include <vector>
​
class myPrint
{
public:
    void operator()(int val)
    {
        cout << val << " ";
    }
};
​
void test01()
{
    vector<int> v;
    v.push_back(20);
    v.push_back(30);
    v.push_back(20);
    v.push_back(40);
    v.push_back(50);
    v.push_back(10);
    v.push_back(20);
​
    cout << "替换前:" << endl;
    for_each(v.begin(), v.end(), myPrint());
    cout << endl;
​
    //将容器中的20 替换成 2000
    cout << "替换后:" << endl;
    replace(v.begin(), v.end(), 20,2000);
    for_each(v.begin(), v.end(), myPrint());
    cout << endl;
}
​
int main() {
​
    test01();
​
    system("pause");
​
    return 0;
}

概要: replace は、条件を満たす間隔内の要素を置き換えます。

5.4.3 replace_if

機能の説明:

  • 条件を満たす区間内の要素を指定された要素に置き換えます

関数プロトタイプ:

  • replace_if(iterator beg, iterator end, _pred, newvalue);

    // 条件に従って要素を置き換え、条件を満たす要素を指定された要素に置き換えます

    // beg はイテレータを開始します

    // 終了反復子

    // _pred述語

    // 新しい要素は newvalue に置き換えられます

例:

#include <algorithm>
#include <vector>
​
class myPrint
{
public:
    void operator()(int val)
    {
        cout << val << " ";
    }
};
​
class ReplaceGreater30
{
public:
    bool operator()(int val)
    {
        return val >= 30;
    }
​
};
​
void test01()
{
    vector<int> v;
    v.push_back(20);
    v.push_back(30);
    v.push_back(20);
    v.push_back(40);
    v.push_back(50);
    v.push_back(10);
    v.push_back(20);
​
    cout << "替换前:" << endl;
    for_each(v.begin(), v.end(), myPrint());
    cout << endl;
​
    //将容器中大于等于的30 替换成 3000
    cout << "替换后:" << endl;
    replace_if(v.begin(), v.end(), ReplaceGreater30(), 3000);
    for_each(v.begin(), v.end(), myPrint());
    cout << endl;
}
​
int main() {
​
    test01();
​
    system("pause");
​
    return 0;
}

概要: replace_if は条件によって検索し、ファンクターを使用して条件を満たす条件を柔軟にフィルターできます。

5.4.4 スワップ

機能の説明:

  • 2 つのコンテナの要素を交換します

関数プロトタイプ:

  • swap(container c1, container c2);

    // 2 つのコンテナの要素を交換します

    // c1 コンテナ 1

    // c2 コンテナ 2

例:

#include <algorithm>
#include <vector>
​
class myPrint
{
public:
    void operator()(int val)
    {
        cout << val << " ";
    }
};
​
void test01()
{
    vector<int> v1;
    vector<int> v2;
    for (int i = 0; i < 10; i++) {
        v1.push_back(i);
        v2.push_back(i+100);
    }
​
    cout << "交换前: " << endl;
    for_each(v1.begin(), v1.end(), myPrint());
    cout << endl;
    for_each(v2.begin(), v2.end(), myPrint());
    cout << endl;
​
    cout << "交换后: " << endl;
    swap(v1, v2);
    for_each(v1.begin(), v1.end(), myPrint());
    cout << endl;
    for_each(v2.begin(), v2.end(), myPrint());
    cout << endl;
}
​
int main() {
​
    test01();
​
    system("pause");
​
    return 0;
}

概要:コンテナを交換する場合、交換されるコンテナは同じタイプである必要があることに注意してください

5.5 一般的な算術生成アルゴリズム

学習目標:

  • よく使われる算術生成アルゴリズムをマスターする

知らせ:

  • 算術生成アルゴリズムは小規模なアルゴリズムであり、使用時にインクルードされるヘッダファイルは#include <numeric>

アルゴリズムの紹介:

  • accumulate// コンテナ要素の累積合計を計算します

  • fill// コンテナに要素を追加します

5.5.1 蓄積

機能の説明:

  • 範囲内のコンテナ要素の累積合計を計算します。

関数プロトタイプ:

  • accumulate(iterator beg, iterator end, value);

    // コンテナ要素の累積合計を計算します

    // beg はイテレータを開始します

    // 終了反復子

    // 値の開始値

例:

#include <numeric>
#include <vector>
void test01()
{
    vector<int> v;
    for (int i = 0; i <= 100; i++) {
        v.push_back(i);
    }
​
    int total = accumulate(v.begin(), v.end(), 0);
​
    cout << "total = " << total << endl;
}
​
int main() {
​
    test01();
​
    system("pause");
​
    return 0;
}

概要:累算を使用する場合、ヘッダー ファイルは数値であることに注意してください。このアルゴリズムは非常に実用的です。

5.5.2 塗りつぶし

機能の説明:

  • コンテナに指定された要素を埋め込みます

関数プロトタイプ:

  • fill(iterator beg, iterator end, value);

    // コンテナに要素を埋め込みます

    // beg はイテレータを開始します

    // 終了反復子

    // 値を埋めた値

例:

#include <numeric>
#include <vector>
#include <algorithm>
​
class myPrint
{
public:
    void operator()(int val)
    {
        cout << val << " ";
    }
};
​
void test01()
{
​
    vector<int> v;
    v.resize(10);
    //填充
    fill(v.begin(), v.end(), 100);
​
    for_each(v.begin(), v.end(), myPrint());
    cout << endl;
}
​
int main() {
​
    test01();
​
    system("pause");
​
    return 0;
}

概要: fill を使用して、コンテナー間隔内の要素を指定された値で埋めます。

5.6 共通の集合アルゴリズム

学習目標:

  • よく使われる集合アルゴリズムをマスターする

アルゴリズムの紹介:

  • set_intersection// 2 つのコンテナの交差部分を見つけます

  • set_union// 2 つのコンテナの結合を見つけます

  • set_difference// 2 つのコンテナの違いを見つけます

5.6.1 set_intersection

機能の説明:

  • 2 つのコンテナの交差部分を見つける

関数プロトタイプ:

  • set_intersection(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);

    // 2 つのセットの共通部分を見つけます

    //注: 2 つのコレクションは順序付けされたシーケンスである必要があります

    // beg1 コンテナ 1 開始イテレータ // end1 コンテナ 1 終了イテレータ // beg2 コンテナ 2 開始イテレータ // end2 コンテナ 2 終了イテレータ // dest 宛先コンテナ開始イテレータ

例:

#include <vector>
#include <algorithm>
​
class myPrint
{
public:
    void operator()(int val)
    {
        cout << val << " ";
    }
};
​
void test01()
{
    vector<int> v1;
    vector<int> v2;
    for (int i = 0; i < 10; i++)
    {
        v1.push_back(i);
        v2.push_back(i+5);
    }
​
    vector<int> vTarget;
    //取两个里面较小的值给目标容器开辟空间
    vTarget.resize(min(v1.size(), v2.size()));
​
    //返回目标容器的最后一个元素的迭代器地址
    vector<int>::iterator itEnd = 
        set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());
​
    for_each(vTarget.begin(), itEnd, myPrint());
    cout << endl;
}
​
int main() {
​
    test01();
​
    system("pause");
​
    return 0;
}

要約:

  • 2 つのセットの共通部分に必要な順序付けされたシーケンス

  • ターゲット コンテナは、スペースを空けるために2 つのコンテナから小さい値を取得する必要があります。

  • set_intersection の戻り値は、交差部分の最後の要素の位置です。

5.6.2 set_union

機能の説明:

  • 2 つの集合の和集合を求めます

関数プロトタイプ:

  • set_union(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);

    // 2 つの集合の和集合を求めます

    //注: 2 つのコレクションは順序付けされたシーケンスである必要があります

    // beg1 コンテナ 1 開始イテレータ // end1 コンテナ 1 終了イテレータ // beg2 コンテナ 2 開始イテレータ // end2 コンテナ 2 終了イテレータ // dest 宛先コンテナ開始イテレータ

例:

#include <vector>
#include <algorithm>
​
class myPrint
{
public:
    void operator()(int val)
    {
        cout << val << " ";
    }
};
​
void test01()
{
    vector<int> v1;
    vector<int> v2;
    for (int i = 0; i < 10; i++) {
        v1.push_back(i);
        v2.push_back(i+5);
    }
​
    vector<int> vTarget;
    //取两个容器的和给目标容器开辟空间
    vTarget.resize(v1.size() + v2.size());
​
    //返回目标容器的最后一个元素的迭代器地址
    vector<int>::iterator itEnd = 
        set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());
​
    for_each(vTarget.begin(), itEnd, myPrint());
    cout << endl;
}
​
int main() {
​
    test01();
​
    system("pause");
​
    return 0;
}

要約:

  • 2 つのセットの和集合を見つけるために必要な順序付けされたシーケンス

  • ターゲット コンテナはスペースを空けるために2 つのコンテナを追加する必要があります

  • set_union の戻り値は、共用体の最後の要素の位置です。

5.6.3 set_difference

機能の説明:

  • 2 つのセットの差を求めます

関数プロトタイプ:

  • set_difference(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);

    // 2 つのセットの差を求めます

    //注: 2 つのコレクションは順序付けされたシーケンスである必要があります

    // beg1 コンテナ 1 開始イテレータ // end1 コンテナ 1 終了イテレータ // beg2 コンテナ 2 開始イテレータ // end2 コンテナ 2 終了イテレータ // dest 宛先コンテナ開始イテレータ

例:

#include <vector>
#include <algorithm>
​
class myPrint
{
public:
    void operator()(int val)
    {
        cout << val << " ";
    }
};
​
void test01()
{
    vector<int> v1;
    vector<int> v2;
    for (int i = 0; i < 10; i++) {
        v1.push_back(i);
        v2.push_back(i+5);
    }
​
    vector<int> vTarget;
    //取两个里面较大的值给目标容器开辟空间
    vTarget.resize( max(v1.size() , v2.size()));
​
    //返回目标容器的最后一个元素的迭代器地址
    cout << "v1与v2的差集为: " << endl;
    vector<int>::iterator itEnd = 
        set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());
    for_each(vTarget.begin(), itEnd, myPrint());
    cout << endl;
​
​
    cout << "v2与v1的差集为: " << endl;
    itEnd = set_difference(v2.begin(), v2.end(), v1.begin(), v1.end(), vTarget.begin());
    for_each(vTarget.begin(), itEnd, myPrint());
    cout << endl;
}
​
int main() {
​
    test01();
​
    system("pause");
​
    return 0;
}

要約:

  • 2 つのセットを減算するために必要な順序付けされたシーケンス

  • ターゲット コンテナは、スペースを空けるために2 つのコンテナから大きい方の値を取得する必要があります。

  • set_difference の戻り値は、差分セット内の最後の要素の位置です。

おすすめ

転載: blog.csdn.net/weixin_58176527/article/details/127792859