ビデオリンク: https://www.bilibili.com/video/BV1et411b73Z/この
メモ: P167-263
C++ でプログラミングが改善される
- このステージでは主に C++ に焦点を当てます汎用プログラミングそしてSTLテクノロジーを詳細に説明し、C++ のより深い使い方を探ります。
1 テンプレート
1.1 テンプレートの概念
テンプレートを使用してユニバーサルモールドを作成し、再利用性を大幅に向上させます。
たとえば、人生におけるテンプレート
1 インチの写真テンプレート:
PPTテンプレート:
テンプレートの機能:
- テンプレートは直接使用することはできません。これは単なるフレームワークです
- テンプレートは普遍的ですが、全能ではありません
1.2 関数テンプレート
-
C++ における別のプログラミングのアイデアは次のように呼ばれます。汎用プログラミング、使用される主なテクノロジーはテンプレートです
-
C++ は、関数テンプレートとクラス テンプレートという2 つのテンプレート メカニズムを提供します。
1.2.1 関数テンプレートの構文
関数テンプレート関数:
関数の戻り値の型と仮パラメータの型を指定する必要がなく、仮想型で表される一般的な関数を作成します。
文法:
template<typename T>
函数声明或定义
説明する:
テンプレート—テンプレート作成の宣言
タイプ名— その背後にあるシンボルはデータ型であり、クラスに置き換えることができるようです。
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>
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 通常関数および関数テンプレートの呼び出し規則
呼び出しルールは次のとおりです。
- 関数テンプレートと通常の関数の両方が実装できる場合は、通常の関数が最初に呼び出されます。
- 空のテンプレートパラメータリストを使用して関数テンプレートを強制的に呼び出すことができます
- 関数テンプレートはオーバーロードすることもできます
- 関数テンプレートの方がより適切な一致を生成できる場合、関数テンプレートが最初に呼び出されます。
例:
//普通函数与函数模板调用规则
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("even", 23);
P1.showPerson();
}
int main() {
test01();
system("pause");
return 0;
}
概要: クラス テンプレートと関数テンプレートの構文は似ており、宣言テンプレート テンプレートの後にクラスを追加します。このタイプをクラス テンプレートと呼びます。
1.3.2 クラステンプレートと関数テンプレートの違い
クラス テンプレートと関数テンプレートの間には、主に 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つの入力方法:
- 渡される型を指定- オブジェクトのデータ型を直接表示します
- パラメータのテンプレート化- オブジェクト内のパラメータを渡すためのテンプレートに変換します。
- クラス全体をテンプレート化— このオブジェクト タイプをテンプレート化して渡す
例:
#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;
}
- 1. 受信タイプを指定します
- 2. パラメータのテンプレート化
- 3. クラス全体をテンプレート化する
要約:
- クラス テンプレートを通じて作成されたオブジェクトは、次の 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; // 编译成功的
}
//如果想灵活指定出父类中T的类型,子类也需变为类模板
//类模板继承类模板 ,可以用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 は合意された名前であり、必須ではありません)。
例 1:
例 2:
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、全局函数配合友元 类外实现 - 先做函数模板声明,下方在做函数模板定义,在做友元
//提前让编译器知道Person类的存在
template<class T1, class T2>
class Person;
// 类外实现
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;
this->pAddress = new T[this->m_Capacity];
}
//拷贝构造
MyArray(const MyArray & arr)
{
this->m_Capacity = arr.m_Capacity;
this->m_Size = arr.m_Size;
//指针不能这样赋值,浅拷贝问题,会导致堆区数据重复释放
//this->pAddress = arr.pAddress;
//解决方法:深拷贝,重新在堆区创建一个空间
this->pAddress = new T[arr.m_Capacity];
//将arr中的数据都拷贝过来
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->pAddress = NULL;
}
// 深拷贝
this->m_Capacity = myarray.m_Capacity;
this->m_Size = myarray.m_Size;
this->pAddress = new T[myarray.m_Capacity];
for (int i = 0; i < this->m_Size; i++) {
//this->pAddress[i] = myarray[i];
this->pAddress[i] = myarray.pAddress[i];
}
return *this; //自身返回
}
//通过下标方式访问数组中的元素 arr[]
//重载[] 操作符 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;
}
//直接长度-1,访问不到最后元素,逻辑删除
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 が誕生しました。
1.
カプセル化: 同様の属性や動作を持ついくつかの物事を抽象化し、それらの物事や事柄を全体として実現します。これにより再利用性が向上します。
2. 継承: サブクラスは親クラスから継承します。親クラスのすべてのプロパティと動作を取得します。改めて述べる必要はありません。これにより、コードの再利用も向上します。
3. ポリモーフィズム: 関数名には複数のインターフェイスがあります。オブジェクトが異なるため、名前も同じです。親クラス ポインタはサブクラス オブジェクトを指します。オブジェクトが異なる方法で作成された場合、同じインターフェイスを呼び出すと異なる形式が生成されます。これにより、再利用性も向上します。
2.2 STLの基本概念
- STL(標準テンプレートライブラリ、標準テンプレートライブラリ)
- STLは大きく分けて、コンテナ(container) アルゴリズム(algorithm) イテレータ(iterator)
- コンテナとアルゴリズムはイテレータを通じてシームレスに接続されます。
- STL のほとんどすべてのコードは、テンプレート クラスまたはテンプレート関数を使用します。
2.3 STL の 6 つの主要コンポーネント
STL は、大きく次の 6 つの主要コンポーネントに分かれています。コンテナ、アルゴリズム、イテレータ、ファンクタ、アダプタ(アダプタ)、空間コンフィギュレータ
- コンテナ: データの保存には、ベクター、リスト、デキュー、セット、マップなどのさまざまなデータ構造が使用されます。
- アルゴリズム: ソート、検索、コピー、for_each などの一般的に使用されるさまざまなアルゴリズム。
- イテレータ: コンテナとアルゴリズムの間の接着剤として機能します。
- ファンクター: 関数のように動作し、アルゴリズムの戦略として使用できます。
- アダプター: コンテナー、ファンクター、またはイテレーターのインターフェースを装飾するために使用されるもの。
- スペースコンフィギュレーター: スペースの構成と管理を担当します。
2.4 STL のコンテナ、アルゴリズム、イテレータ
コンテナ:物を保管する場所
STLコンテナは、最も広く使用されているデータ構造の一部を実装しています。
一般的に使用されるデータ構造: 配列、リンク リスト、ツリー、スタック、キュー、セット、マッピング テーブルなど。これらのコンテナは、シーケンシャル コンテナと連想コンテナの
2 つのタイプに分けられます。
- 序列式容器: 値の順序を強調し、シーケンス コンテナー内の各要素の位置は固定されています。つまり、広告掲載オーダーは彼のオーダーです
- 例: 広告掲載オーダーは 13542、コンテナ オーダーも 13542
- 連想コンテナ:バイナリ ツリー構造では、要素間に厳密な物理的順序関係はありません。つまり、挿入時にソートが実行されます。
- 例: 広告掲載オーダーは 13542、コンテナ オーダーは 12345
アルゴリズム: 問題の解決策も、
論理的または数学的問題を解決するための限られた数のステップです。この主題はアルゴリズムと呼ばれます。アルゴリズムは、質的変化アルゴリズムと非質的変化アルゴリズム
に分けられます。
-
質的変化アルゴリズム: 操作中に変更される間隔内の要素の内容を指します。たとえば、コピー、置換、削除などです。
-
非定性アルゴリズム: 間隔内の要素の内容が、検索、カウント、トラバース、極値の検索などの操作中に変更されないことを意味します。
イテレータ: コンテナとアルゴリズムの間の接着剤。つまり、アルゴリズムはコンテナ内の要素にアクセスするためにイテレータを使用する必要があります。
コンテナの内部表現を公開せずに、コンテナに含まれる要素に順次アクセスする方法を提供します。
各コンテナには独自のイテレータがあります
イテレータの使い方はポインタと非常に似ており、初心者の段階では、まずイテレータがポインタであることを理解できます。
イテレータの型:
タイプ | 関数 | サポート計算 |
---|---|---|
入力反復子 | データへの読み取り専用アクセス | 読み取り専用。++、==、をサポートします。= |
出力反復子 | データへの書き込み専用アクセス | 書き込みのみ、++ をサポート |
前方反復子 | 読み取りおよび書き込み操作、およびイテレータを進めることができます | 読み取りと書き込み、++、== をサポート! = |
双方向反復子 | 読み取りおよび書き込み操作、および順方向および逆方向の操作が可能 | 読み取りと書き込み、++、-、をサポート |
ランダムアクセス反復子 | 読み取りおよび書き込み操作、ジャンプ方式であらゆるデータにアクセスできる、最も強力なイテレーター | 読み取りと書き込み、++、-、[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("a", 1);
Person p2("b", 2);
Person p3("c", 3);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
for (vector<Person>::iterator it = v.begin(); it != v.end(); it++) {
cout << "Name:" << (*it).mName << " Age:" << (*it).mAge << endl;
//cout << "Name:" << it->mName << " Age:" << it->mAge << endl;
}
}
//存放自定义数据类型对象 指针
void test02() {
vector<Person*> v;
//创建数据
Person p1("aa", 1);
Person p2("bb", 2);
Person p3("cc", 3);
v.push_back(&p1); //把地址保存 故加上&取址符号
v.push_back(&p2);
v.push_back(&p3);
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* 型のコンテナとして管理します。
特徴:
文字列クラスは多くのメンバーメソッドを内部的にカプセル化します。
例: 検索、コピー、削除、置換、挿入
String は char* によって割り当てられたメモリを管理します。範囲外のコピーや範囲外の値について心配する必要はありません。責任はクラスによって内部的に処理されます。
3.1.2 文字列コンストラクター
コンストラクターのプロトタイプ:
string();
//空の文字列を作成します例: string str;
string(const char* s);
//文字列 s で初期化します。string(const string& str);
// 1 つの文字列オブジェクトを使用して別の文字列オブジェクトを初期化します。つまり、コピー構築します。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'); //创建10个a
cout << "str4 = " << s4 << 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;
// s の最初の n 文字の最初の位置を pos 位置から検索します。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");
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;
//文字列と比較しますint compare(const char *s) const;
//文字列と比較します
例:
//字符串比较
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";
//1、通过[]访问单个字符
for (int i = 0; i < str.size(); i++)
{
cout << str[i] << " ";
}
cout << endl;
//2、通过at方式访问单个字符
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;
}
概要: 文字列 string 内の 1 文字にアクセスするには、[ ] または at を使用する 2 つの方法があります。
3.1.8 文字列の挿入と削除
機能の説明:
- 文字列への文字の挿入と削除
関数プロトタイプ:
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;
}
**まとめ:** substring関数を柔軟に活用することで、実際の開発において有効な情報を取得できる
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()
{
//1、默认构造 无参构造
vector<int> v1;
//容器中添加数据
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
printVector(v1);
//2、通过区间方式进行构造
//左闭右开 end指向最后一个元素的下一个位置
//vector容器中可以通过begin和end迭代器传入 把两个迭代器间的数值传入
vector<int> v2(v1.begin(), v1.end());
printVector(v2);
//3、n个elem方式构造
vector<int> v3(10, 100); //10个100
printVector(v3);
//拷贝构造
vector<int> v4(v3);
printVector(v4);
}
int main() {
test01();
system("pause");
return 0;
}
要約: Vector のさまざまな構築方法は比較するものではなく、柔軟に使用してください。
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);
//1、赋值 operator=
vector<int>v2;
v2 = v1;
printVector(v2);
//2、赋值 assign
vector<int>v3;
v3.assign(v1.begin(), v1.end());
printVector(v3);
//3、赋值 assign n个elem方式赋值
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;
}
要約:
- 空かどうかを判断します - 空です
- 要素の数 — サイズを返します。
- コンテナの容量 (capacity) を返します。
- サイズ変更 — サイズ変更
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);
printVector(v1);
//尾删
v1.pop_back();
printVector(v1);
//插入 提供一个迭代器进行插入 第一个参数是迭代器
v1.insert(v1.begin(), 100);
printVector(v1);
v1.insert(v1.begin(), 2, 1000); //在头部插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);
}
//1、利用[]访问数组中元素
for (int i = 0; i < v1.size(); i++)
{
cout << v1[i] << " ";
}
cout << endl;
//2、利用at方式访问元素
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;
cout << "v的大小为:" << v.size() << endl;
v.resize(3); //重新指定大小
cout << "v的容量为:" << v.capacity() << endl;
cout << "v的大小为:" << v.size() << endl;
//收缩内存
vector<int>(v).swap(v); //匿名对象
cout << "v的容量为:" << v.capacity() << endl;
cout << "v的大小为:" << v.size() << endl;
}
int main() {
test01();
test02();
system("pause");
return 0;
}
概要: swap を使用すると、2 つのコンテナーを交換可能にし、実質的なメモリ削減効果を実現できます。
3.2.8 ベクトル予約スペース
機能の説明:
- 動的にベクトルの容量を拡張する際のベクトルの拡張回数を削減します。
関数プロトタイプ:
-
reserve(int len);
//コンテナは len 要素の長さを予約しますが、予約された位置は初期化されておらず、要素はアクセスできません。メモリを確保するだけで、メモリ内のデータは初期化されていないため、アクセスすることはできません。
例:
#include <vector>
void test01()
{
vector<int> v;
//会节省动态开辟操作,利用reserve直接一开始预留空间
//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 コンテナの基本概念
関数:
- 両端配列。ヘッドエンドで挿入および削除操作を実行できます。
デックとベクターの違い:
- Vectorはヘッダーの挿入や削除の効率が悪く、データ量が大きくなるほど効率が悪くなります。
- 相対的に言えば、deque はヘッダーの挿入と削除において、vector よりも高速です。
- 要素にアクセスするベクターの速度は、deque の速度よりも高速です。これは、この 2 つの内部実装に関係しています。
- 次のバッファにアクセスするときは、アドレスを介して次のバッファ内のデータにアクセスする必要があります。
deque の内部動作原理:
- 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) //加上const
{
//此时迭代器iterator,要修改成只读的迭代器const_iterator
for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
//*it=100; //容器里中的数据不可以修改了
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);
//operator= 赋值
deque<int>d2;
d2 = d1;
printDeque(d2);
//assign 赋值
deque<int>d3;
d3.assign(d1.begin(), d1.end());
printDeque(d3);
//n个elem赋值
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 には容量の概念がありません
- deque は新しいバッファを無限に開くことができます。中央コントローラーは、スペースの小さなセグメントを維持するためにアドレスを追加するだけで済みます。
- ベクトル メモリ空間は連続的ですが、デキュー メモリ空間は連続的ではありません。
- Vector ではスペースが 1 つしかありませんが、deque では複数の小さなスペースで構成されており、小さなスペースがいっぱいになった場合にのみ新しいスペースが作成され、空のスペースは削除されます。
- 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);
//n 要素データを pos 位置に挿入します。戻り値はありません。 -
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> d1;
d1.push_back(10);
d1.push_back(20);
d1.push_front(100);
d1.push_front(200);
// 200 100 10 20
printDeque(d1);
//insert插入
// 1000 200 100 10 20
d.insert(d1.begin(), 1000);
printDeque(d1);
// 10000 10000 1000 200 100 10 20
d1.insert(d1.begin(), 2,10000);
printDeque(d1);
//按照区间进行插入
deque<int>d2;
d2.push_back(1);
d2.push_back(2);
d2.push_back(3);
// 1 2 3 10000 10000 1000 200 100 10 20
d1.insert(d1.begin(), d2.begin(), d2.end());
printDeque(d1);
}
//删除
void test03()
{
deque<int> d3;
d3.push_back(10);
d3.push_back(20);
d3.push_front(100);
d3.push_front(200);
// 200 100 10 20
printDeque(d3);
deque<int>::iterator it = d3.begin();
it++;
d3.erase(it);
// 200 10 20
printDeque(d3);
//清空
d3.erase(d3.begin(), d3.end());
d3.clear();
printDeque(d3);
}
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)
//開始間隔と終了間隔の要素を並べ替えます- ランダム アクセス反復子をサポートするコンテナーの場合、ソート アルゴリズムを使用してコンテナーを直接ソートできます。
- ベクターコンテナはソートによってソートすることもできます
例:
#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 実装手順
- 5 人のプレーヤーを作成し、ベクターに配置します。
- ベクター コンテナーを走査し、各プレーヤーを取り出し、for ループを実行して、10 人の審査員のスコアを deque コンテナーに保存します。
- 並べ替えアルゴリズムは、両端キュー コンテナ内のスコアを並べ替え、最高スコアと最低スコアを削除します。
- deque コンテナーが 1 回走査され、合計スコアが累積されます。
- 平均点を得る
サンプルコード:
//选手类
class Person
{
public:
Person(string name, int score)
{
this->m_Name = name;
this->m_Score = score;
}
string m_Name; //姓名
int m_Score; //平均分
};
//1、创建5名选手
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);
}
}
//2、给5名选手打分
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 【rand() % 41】表示0~40
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;
}
}
//3、显示最后得分
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 スタックの基本概念
コンセプト:スタックとは先入れ後出し(先入れ後出し、FILO) データ構造、アウトレットが 1 つだけある
スタック内外部から使用できるのは最上位の要素のみです、つまりスタックトラバース動作は許可されません
スタックへのデータの入力は、スタックへのプッシュと呼ばれます。 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
-
キューからデータをデキューすることをデキューと呼びます
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("isak", 11);
Person p2("even", 22);
Person p3("eva", 33);
//向队列中添加元素 入队操作
q.push(p1);
q.push(p2);
q.push(p3);
//队列不提供迭代器,更不支持随机访问
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;
}
要約:
- エンキュー — プッシュ
- デキュー — ポップ
- キューの先頭要素 (先頭) を返します。
- キューの最後の要素を返します - 戻る
- キューが空かどうかを判断します - 空です
- 戻りキューのサイズ — サイズ
3.7 リストコンテナ
3.7.1 リストの基本概念
-
機能: データのチェーンストレージ
-
リンク リスト(リスト) は、物理ストレージ ユニット上の非連続ストレージ構造であり、データ要素の論理的順序は、リンク リスト内のポインタ リンクによって実装されます。
-
リンク リストの構成: リンク リストは一連のノードで構成されます。
-
ノードの構成: 1 つはデータ要素を保存するデータフィールド、もう一方は次のノードアドレスを保存するポインタフィールド
-
STL のリンク リストは双方向の循環リンク リストです。
- リンク リストの格納方法は連続したメモリ空間ではない、リンク リストのイテレータは前方および後方の移動のみをサポートし、双方向イテレータになります。
リストの利点:
- 動的ストレージ割り当ては、メモリの無駄やオーバーフローを回避するために使用されます。
- リンク リストでは、多数の要素を移動せずにポインタを変更するだけで挿入や削除の操作を実行できるため、非常に便利です。
リストの欠点:
- リンク リストは柔軟性がありますが、多くのスペース (ポインター フィールド) と時間 (トラバーサル) を消費します。
リストには重要なプロパティがあります。挿入操作も削除操作も、元のリスト反復子を無効にすることはありません。これはベクトルには当てはまりません。
概要: STL で最もよく使用される 2 つのコンテナーは List と Vector であり、それぞれに独自の長所と短所があります。
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);
//赋值 operator=
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);//n 個の elem データを pos 位置に挿入します。戻り値はありません。
- 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);
// 300 200 100 10 20 30
printList(L);
//尾删
L.pop_back();
// 300 200 100 10 20
printList(L);
//头删
L.pop_front();
// 200 100 10 20
printList(L);
//插入
list<int>::iterator it = L.begin();
L.insert(++it, 1000);
// 200 1000 100 10 20
printList(L);
//删除
it = L.begin();
L.erase(++it);
// 200 100 10 20
printList(L);
//移除
L.push_back(10000);
L.push_back(10000);
L.push_back(10000);
// 200 100 10 20 10000 10000 10000
printList(L);
L.remove(10000);
// 200 100 10 20
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; //错误 不支持[]方式访问数据
//原因是list本质是链表,不是连续线性空间存储数据,迭代器也是不支持随机访问的
cout << "第一个元素为: " << L1.front() << endl;
cout << "最后一个元素为: " << L1.back() << endl;
//list容器的迭代器是双向迭代器,不支持随机访问
list<int>::iterator it = L1.begin();
it++; //支持,双向迭代器只支持++和--操作
cout << "输出迭代器it++值:" << *it << endl;
//it = it + 1;//错误,不支持随机访问,即使是+1
}
int main() {
test01();
system("pause");
return 0;
}
要約:
- リスト コンテナー内のデータには、[] または at を介してアクセスすることはできません。
- 最初の要素 (フロント) を返します。
- 最後の要素を返す — 戻る
3.7.7 リストの反転とソート
機能の説明:
- コンテナ内の要素を反転し、コンテナ内のデータを並べ替えます。
関数プロトタイプ:
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)
{
//因为是int类型的,所以给两个int数据
//降序 就让第一个数 > 第二个数
//返回真或假
//可以结合冒泡排序进行理解,相当于对比两个相邻的数,然后交换(true)和不交换(false)由自己定夺
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.begin(),L.end());
//不支持随机访问迭代器的容器,内部会提供对应一些算法
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 は連想コンテナであり、基礎となる構造はバイナリ ツリーを使用して実装されます。
セットとマルチセットの違い:
- セットではコンテナ内での重複は許可されません要素
- マルチセットではコンテナ内での重複が許可されます要素
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);
//所有元素插入时,自动被排序。 不允许插入重复值【不报错,但插入不成功】
//10 20 30 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(30);
s1.insert(10);
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);
// キーが存在するかどうかを確認し、存在する場合はキーの要素のイテレータを返し、存在しない場合は return 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 (反復子を返す)
- 統計 — カウント (セットの場合、結果は 0 または 1)
3.8.6 セットとマルチセットの違い
学習目標:
- セットとマルチセットの違いをマスターする
違い:
- セットでは重複データを挿入できませんが、マルチセットでは挿入できます。
- set がデータを挿入すると、挿入が成功したかどうかを示す挿入結果が返されます。
- マルチセットはデータを検出しないため、重複したデータが挿入される可能性があります
例:
#include <set>
//set和multiset区别
void test01()
{
set<int> s;
pair<set<int>::iterator, bool> ret = s.insert(10);
cout << "————set容器第一次插入10————" << endl;
if (ret.second) {
//ret.second 取到第二个bool类型值,true的话表示插入成功
cout << "第一次插入成功!" << endl;
}
else {
cout << "第一次插入失败!" << endl;
}
cout << "————set容器第二次插入10————" << endl;
ret = s.insert(10);
if (ret.second) {
cout << "第二次插入成功!" << endl;
}
else {
cout << "第二次插入失败!" << endl;
}
cout << "————multiset容器插入2个10————" << 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 を使用できます。
- 重複したデータを挿入する必要がある場合は、マルチセットを使用してください
3.8.7 ペアの作成
機能の説明:
- データはペアで表示され、ペアを使用して 2 つのデータを返します
作成する 2 つの方法:
pair<type, type> p ( value1, value2 );
pair<type, type> p = make_pair( value1, value2 );
例:
#include <string>
//pair对组创建
void test01()
{
//第一种方式
pair<string, int> p("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;
}
要約:
ペアを作成するには 2 つの方法がありますが、1 つだけ覚えておいてください。
3.8.8 セットコンテナの仕分け
学習目標:
- セット コンテナーのデフォルトの並べ替えルールは小さいものから大きいものまであります。並べ替えルールを変更する方法について説明します。
主な技術的なポイント:
- ファンクターを使用すると、並べ替えルールを変更できます
例 1 セットは組み込みデータ型を格納します
#include <set>
//仿函数 本质上是一个类型
class MyCompare
{
public:
//第一个()表示重载的符号
//第二个()表示重载符号函数体的参数列表
bool operator()(int v1, int v2) const{
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);
//set<int,MyCompare>中的两个形参都是数据类型(int、double等等),不能放一个函数名
//MyCompare是一种class类型。
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) const
{
//按照年龄进行排序 降序
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;
}
要約:
カスタム データ型の場合、set はデータを挿入する前に照合順序を指定する必要があります。
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; //默认构造
//pair<int, int>(1, 10) 匿名对组
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);
//[]不建议插入,但可以利用key来访问value
//这里m[5]是没有的,但打印的时候会,自动的创建:key=5 value=0
cout << m[5] << endl;
//利用key访问value
cout << m[4] << endl; //通过key找到对应value值40
//删除
m.erase(m.begin());
printMap(m);
//按照key删除
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);
// キーが存在するかどうかを確認し、存在する場合はキーの要素のイテレータを返し、存在しない場合は return 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));
m.insert(pair<int, int>(3, 40));
//查找
map<int, int>::iterator pos = m.find(3);
if (pos != m.end())
{
cout << "找到了元素 key = " << (*pos).first << " value = " << (*pos).second << endl;
}
else
{
cout << "未找到元素" << endl;
}
//统计
//map不允许插入重复key元素,count统计而言 结果要么是0 要么是1
int num = m.count(3);
cout << "m中key=3的数量为:" << 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) const{
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 実装手順
- 10 人の従業員を作成し、ベクターに配置します。
- ベクター コンテナを走査し、各従業員を取り出し、ランダムにグループ化します。
- グループ化した後、従業員の部門番号をキーとして、特定の従業員を値として使用し、それらをマルチマップ コンテナーに入れます。
- 従業員情報を部門ごとに表示
ケースコード:
#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;
};
//1、创建员工分组
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);
}
}
//2、员工分组
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));
}
}
//3、分组显示员工
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 関数オブジェクトの概念
コンセプト:
- 関数呼び出し演算子をオーバーロードするクラス。そのオブジェクトは関数オブジェクトと呼ばれることがよくあります。
- 関数オブジェクトがoverloaded ()を使用すると、ファンクターとも呼ばれる関数呼び出しのように動作します。
自然:
関数オブジェクト (ファンクター) はクラスです、関数ではありません
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; //20
}
//2、函数对象超出普通函数的概念,函数对象可以有自己的状态
class MyPrint
{
public:
MyPrint()
{
this->count = 0;
}
void operator()(string test)
{
cout << test << endl;
this->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 型のファンクターを返します。述語と呼ばれる
- if 演算子()パラメータを受け入れます、その後、それは呼び出されます単項述語
- if 演算子()2 つのパラメータを受け入れます、その後、それは呼び出されますバイナリ述語
4.2.2 単項述語
例:
#include <vector>
#include <algorithm>
//返回bool类型的仿函数称为谓词
//1.一元谓词 operator()接受一个参数
class GreaterFive{
public:
bool operator()(int val) const{
return val > 5;
}
};
void test01() {
vector<int> v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
//查找容器中有没有大于5的数字
//GreaterFive() 匿名函数对象
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)const
{
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 算術関手
機能の説明:
- 四則演算を実装する
- このうち、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; //-50
}
//plus 二元仿函数 加法
void test02()
{
//默认传入的是同一种类型的数
plus<int> p;
cout << p(10, 20) << endl; //30
}
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容器扩大到v1容器大小
v2.resize(v.size());
//transform 搬运算法
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 はイテレータを開始します
// end イテレータを終了します
// _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 はイテレータを開始します
// end イテレータを終了します
// 検索する値要素
例:
#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 はイテレータを開始します
// end イテレータを終了します
// _Pred 関数または述語 (bool 型ファンクターを返す)
例:
#include <algorithm>
#include <vector>
#include <string>
class GreaterFive
{
public:
bool operator()(int val) const
{
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) const
{
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 はイテレータを開始します
// end イテレータを終了します
例:
#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 の隣接する_find アルゴリズムを使用することを忘れないでください。
5.2.4 バイナリ検索
機能の説明:
- 指定された要素が存在するかどうかを調べる
関数プロトタイプ:
-
bool binary_search(iterator beg, iterator end, value);
//指定された要素を検索し、見つかった場合は true を返し、それ以外の場合は false を返します。
// 注:順序付けされていないシーケンスでは使用できません
// beg はイテレータを開始します
// end イテレータを終了します
// 検索する値要素
例:
#include <algorithm>
#include <vector>
void test01()
{
vector<int>v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
//二分查找 查找容器中是否有2元素
//注意:这个容器必须是有序且升序的序列 降序序列也不行
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 はイテレータを開始します
// end イテレータを終了します
// 統計の値要素
例:
#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 はイテレータを開始します
// end イテレータを終了します
// _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 を使用し、条件に基づく統計には count_if を使用します。
5.3 一般的に使用される並べ替えアルゴリズム
学習目標:
- よく使用される並べ替えアルゴリズムをマスターする
アルゴリズムの紹介:
sort
//コンテナ内の要素を並べ替えるrandom_shuffle
//指定された範囲内の要素をシャッフルし、順序をランダムに調整しますmerge
// コンテナ要素はマージされ、別のコンテナに保存されますreverse
//指定した範囲の要素を反転します
5.3.1 並べ替え
機能の説明:
- コンテナ内の要素を並べ替える
関数プロトタイプ:
-
sort(iterator beg, iterator end, _Pred);
// 要素を値で検索し、見つかった場合は指定された位置のイテレータを返し、見つからない場合は終了イテレータの位置を返します。
// beg はイテレータを開始します
// end イテレータを終了します
// _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 はイテレータを開始します
// end イテレータを終了します
例:
#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;
}
概要: シャッフル アルゴリズムはrandom_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 はイテレータを開始します
// end イテレータを終了します
例:
#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 はイテレータを開始します
// end イテレータを終了します
// 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 はイテレータを開始します
// end イテレータを終了します
// 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 はイテレータを開始します
// end イテレータを終了します
// _pred述語
// 新しい要素を新しい値に置き換えます
例:
#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 はイテレータを開始します
// end イテレータを終了します
// 値の開始値
例:
#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 はイテレータを開始します
// end イテレータを終了します
// 値を埋めた値
例:
#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 つのコンテナから大きい方の値を取得する必要があります。