目录
1 模板
1.1 模板的概念
1.2 函数模板
1.2.1 函数模板语法
1.2.2 函数模板注意事项
1.2.3 函数模板的案例
1.2.4 普通函数与函数模板的区别
1.2.5 普通函数与函数模板的调用规则
1.2.6 模板的局限性
1.3 类模板
1.3.1 类模板语法
1.3.2 类模板与函数模板区别
1.3.3 类模板中成员函数创建时机
1.3.4 类模板对象做函数参数
1.3.5 类模板与继承
1.3.6 类模板成员函数类外实现
1.3.7 类模板分文件编写
1.3.8 类模板与友元
1.3.9 类模板案例
2 STL初识
2.1 STL的诞生
2.2 STL基本概念
2.3 STL六大组件
2.4 STL中容器、算法、迭代器
2.5 容器算法迭代器初始
2.5.1 vector存放内置数据类型
2.5.2 vector存放自定义数据类型
2.5.3 vector容器嵌套容器
3 STL常用容器
3.1 string容器
3.1.1 string基本概念
3.1.2 string构造函数
3.1.3 string赋值操作
3.1.4 string字符串拼接
3.1.5 string查找和替换
3.1.6 string字符串比较
3.1.7 string字符存取
3.1.8 string插入和删除
3.1.9 string子串
3.2 vector 容器
3.2.1 vector基本概念
3.2.2 vector构造函数
3.2.3 vector赋值操作
3.2.4 vector容量和大小
3.2.5 vector插入和删除
3.2.6 vector数据存取
3.2.7 vector互换容器
3.2.8 vector预留空间
3.3 deque容器
3.3.1 deque容器基本概念
3.3.2 deque构造函数
3.3.2 deque赋值操作
3.3.4 deque大小操作
3.3.5 deque插入和删除
3.3.6 deque数据存取
3.3.7 deque排序
3.4 案例——评委打分
3.5 stack容器
3.5.1 stack基本概念
3.5.2 stack常用接口
3.6 queue容器
3.6.1 queue基本概念
3.6.2 queue常用接口
3.7 list容器
3.7.1 list基本概念
3.7.2 list构造函数
3.7.3 list赋值和交换
3.7.4 list大小操作
3.7.5 list插入和删除
3.7.6 list数据存取
3.7.7 list反转和排序
3.7.8 排序案例
3.8 set容器
3.8.1 set基本概念
3.8.2 set构造和赋值
3.8.3 set大小和交换
3.8.4 set插入和删除
3.8.5 set查找和统计
3.8.6 set和multiset区别
3.8.7 pair对组创建
3.8.8 set容器排序
3.9 map/multimap容器
3.9.1 map基本概念
3.9.2 map构造和赋值
3.9.3 map容器大小和交换
3.9.4 map容器插入和删除
3.9.5 map容器查找和统计
3.9.6 map容器排序
3.10 案例——员工分组
3.10.1 案例描述
3.10.2 实现步骤
4 STL——函数对象
4.1 函数对象
4.1.1 函数对象概念
4.1.2 函数对象使用
4.2 谓词
4.2.1 谓词概念
4.2.2 一元谓词
4.2.3 二元谓词
4.3 内建函数对象
4.3.1 内建函数对象意义
4.3.2 算法仿函数
4.3.3 关系仿函数
4.3.4 逻辑仿函数
5 STL——常用算法
5.1 常用遍历算法
5.1.1 for_each
5.1.2 transform
5.2 常用查找算法
5.2.1 find
5.2.2 find_if
5.2.3 adjacent_find
5.2.4 binary_search
5.2.5 count
5.2.6 count_if
5.3 常用排序算法
5.3.1 sort
5.3.2 random_shuffle
5.3.3 merge
5.3.4 reverse
5.4 常用拷贝和替换算法
5.4.1 copy
5.4.2 replace
5.4.3 replace_if
5.4.4 swap
5.5 常用算术生成算法
5.5.1 accumulate
5.5.2 fill
5.6 常用集合算法
5.6.1 set_intersection
5.6.2 set_union
5.6.3 set_difference
这个阶段的学习只要针对C++泛型编程和STL技术做详细讲解,探讨C++更深层的使用。
模板就是建立通用的模具,大大提高复用性。
特点:模板不能直接使用;
模板不是万能的。
函数模板作用:建立一个通用函数,其函数返回类型和形参可以不具体指定,用一个虚拟的类型来代表。
语法:
解释:
template——声明创建模板;
typename——表面其后面的符号是一种数据类型,可以用class代替。
T——通用的数据类型,名称可以替换,通常为大写字母。
#include
using namespace std;
//函数模板
//两个整形交换函数
void swapInt(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
}
//函数模板
template //声明一个模板,告诉编译器后面代码中紧跟着的T不要报错,T是一个通用数据类型
void mySwap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
//交换两个浮点数型函数
void swapDouble(double& a, double& b)
{
double temp = a;
a = b;
b = temp;
}
void test01()
{
int a = 10;
int b = 20;
//swapInt(a, b);
//利用函数模板交换
//两种方式使用函数模板
//1、自动类型推导
//mySwap(a, b);
//2、显示指定类型
mySwap(a, b);
cout << "a= " << a << endl;
cout << "b= " << b << endl;
double c = 1.23;
double d = 12.23;
swapDouble(c, d);
cout << "c= " << c << endl;
cout << "d= " << d << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
总结:
注意事项:
#include
using namespace std;
//函数模板注意事项
template//typename可以替换成class
void mySwap(T&a,T&b)
{
T temp=a;
a=b;
b=temp;
}
//1、自动类型推导,必须推导出一致的数据类型T,才可以使用
void test01()
{
int a=10;
int b=20;
char c='c';
mySwap(a,b);//正确,可以推导出一致的T
//mySwap(a,c);//错误,推导不出一只的T类型
}
//2、模板必须要确定出T的数据类型,才可以使用
template
void func()
{
cout<<"func 调用"<();//利用先指定类型的方式,给T一个类型,才可以使用该模板
}
int main()
{
//test01();
test02();
system("pause");
return 0;
}
总结:
使用模板时必须确定出通用数据类型T,并且能够推导出一致的类型。
案例描述:
//实现通用 对数组进行排序的函数
//交换函数模板
template
void mySwap(T&a,T&b)
{
T temp=a;
a=b;
b=temp;
}
//排序算法
template
void mySort(T arr[],int len)
{
for(int i=0;i
printArray(T arr[],int len)
{
for(int i=0;i
普通函数与函数模板的区别:(是否发生自动类型转换)
// 1.2.4 普通函数与函数模板的区别
//普通函数
int myAdd01(int a, int b)
{
return a + b;
}
//函数模板
template
T myAdd02(int a, int 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(a, c);//正确,如果用显示指定类型,可以发生隐式类型转换
}
int main()
{
test01();
system("pause");
return 0;
}
调用规则如下:
//1.2.5 普通函数与函数模板的调用
//普通函数与函数模板调用规则
void myPrint(int a, int b)
{
cout << "调用的普通函数" << endl;
}
template
void myPrint(T a, T b)
{
cout << "调用模板" << endl;
}
template
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);//调用函数模板
//(也可以调用普通函数,会发生隐式类型转换 | 而调用模板的话则只用推导类型T)
}
int main()
{
test01();
system("pause");
return 0;
}
总结:既然提供了函数模板,最好就不要提供普通函数,否则容易出现二义性。
局限性:模板的通用性并不是万能的。
//1.2.6 模板的局限性
#include
//模板并不是万能的,有些特定数据类型,需要用具体化方式做特殊实现
//对比两个数据是否相等
class Person
{
public:
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
//姓名
string m_Name;
//年龄
int m_Age;
};
template
bool myCompare(T& a, T& b)
{
if (a == b)
{
return true;
}
else
{
return false;
}
}
//利用具体化person的版本实现代码,具体化优先调用
//具体化,显示具体化的原型,给定以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", 11);
//自定义数据类型,不会调用普通的函数模板
//可以创建具体化的Person数据类型的模板,用于特殊处理这个类型
bool ret = myCompare(p1, p2);//出错,不认识Person类型,(运算符重载可以解决)
if (ret)
{
cout << "p1==p2" << endl;
}
else
{
cout << "p1!=p2" << endl;
}
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
总结:利用具体化的模板,可以解决自定义类型的通用化;
学习模板并不是为了写模板,而是在STL能够运用系统提供的模板。
类模板作用:建立一个通用类,类中的成员数据类型可以不具体制定,用一个虚拟的类型来代表。
语法:template
类
解释:template ——声明创建模板;
typename——表明其别后面的符号是一种数据类型,可以用class代替;
T—— 通用的数据类型,名称可以替换,通常为大写字母。
// 1.3 类模板
#include
template
class Person
{
public:
Person(NameType name,AgeType age)
{
this->m_Name = name;
this->m_Age = age;
}
void showPerson()
{
cout << "name: " << this->m_Name << "age: " << this->m_Name << endl;
}
public:
NameType m_Name;
AgeType m_Age;
};
void test01()
{
//指定NameType为string类型,AgeType为int类型
PersonP1("孙悟空",999);
P1.showPerson();
}
int main()
{
test01();
system("pause");
return 0;
}
总结:类模板和函数模板语法相似,在声明模板template后面加类,此类为类模板。
类模板与函数模板区别主要有两点:
//1.3.2 类模板与函数模板区别
#include
template
class Person
{
public:
Person(NameType name,AgeType age)
{
this->m_Name = name;
this->m_Age = age;
}
void showPerson()
{
cout << "name: " << this->m_Name << "age: " << this->m_Name << endl;
}
public:
NameType m_Name;
AgeType m_Age;
};
//1、类模板没有自动类型推导使用方式
void test01()
{
//Person p("孙悟空", 1000);//错误,无法使用自动类型推导
Personp("孙悟空", 1000);//正确,只能用显示指定类型
p.showPerson();
}
//2、类模板在参数列表中可以有默认参数
void test02()
{
Personp("猪八戒",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 MyClass
{
public:
T obj;
//类模板中的成员函数——在调用时才会分配,obj无法确定数据类型
void func1()
{
obj.showPerson1();
}
void func2()
{
obj.showPerson2();
}
};
void test01()
{
MyClassm;//替换为Person1的数据类型
m.func1();
//m.func2();//不能调用,说明函数调用才会创建成员函数
}
int main()
{
test01();
system("pause");
return 0;
}
总结:类模板中的成员函数并不是一开始就创建,在调用时才去创建。
类模板实例化出的对象,向函数传参的方式。
有一共有三种传入方式:
//1.3.4 类模板对象做函数参数
#include
template
class Person
{
public:
Person(T1 name, T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
void showPerson()
{
cout << "姓名: " << this->m_Name << " 年龄: " << this->m_Age << endl;
}
T1 m_Name;
T2 m_Age;
};
//1、指定传入类型——最常用
void printPerson1(Person&p)//类模板的对象做函数的参数
{
p.showPerson();
}
void test01()
{
Personp("孙悟空", 999);
printPerson1(p);
}
//2、参数模板化
template
void printPerson2(Person&p)
{
p.showPerson();
cout << "T1 的类型为:" << typeid(T1).name() << endl;//输出T1的类型
cout << "T2 的类型为:" << typeid(T2).name() << endl;
}
void test02()
{
Personp("猪八戒", 99);
printPerson2(p);
}
//3、整个类模板化
template
void printPerson(T &p)
{
p.showPerson();
cout << "T的数据类型为:" << typeid(T).name() << endl;//为class Person(为整个类)
}
void test03()
{
Personp("唐僧", 30);
printPerson(p);
}
int main()
{
test01();
test02();
test03();
system("pause");
return 0;
}
总结:通过类模板创建的对象,可以有三种方式向函数中进行传参;
使用比较广泛是第一种:指定传入的类型。
当类模板碰到继承时,需要注意以下几点:
//1.3.5 类模板与继承
template
class Base
{
T m;
};
//class Son:public Base//错误,必须要知道父类中的T类型,才能继承给子类(不知道计算内存时无法计算)
class Son:public Base
{
};
void test01()
{
Son s1;
}
//如果想灵活指定父类中T类型,子类也需要变为类模板
template
class Son2 :public Base
{
public:
Son2()
{
cout << "T1 的类型为:" << typeid(T1).name() << endl;//输出T1的类型
cout << "T2 的类型为:" << typeid(T2).name() << endl;
}
T1 obj;
};
void test02()
{
Son2S2;
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
总结:如果父类时类模板,子类需要指定出父类中T的数据类型。
学习目标:能够掌握类模板中的成员函数类外实现。
#include
using namespace std;
//1.3.6 类模板中的成员函数类外实现
#include
template
class Person
{
public:
//成员函数类内声明
Person(T1 name, T2 age);
/*{
this->m_Namae = name;
this->m_Age = age;
}*/
void showPerson();
/*{
cout << "姓名:" << this->m_Namae << " 年龄:" << endl;
}*/
public:
T1 m_Name;
T2 m_Age;
};
//构造函数类外实现
template
Person::Person(T1 name, T2 age) //模板一般加简尖括号,中为模板的参数模板
{
this->m_Name = name;
this->m_Age = age;
}
//成员函数类外实现
//加作用域Person,
//加模板参数列表,
//T1,T2也需要让编译器知道因此加template
template
void Person::showPerson()
{
cout << "姓名:" << this->m_Name << " 年龄:"<m_Age << endl;
}
void test01()
{
PersonP("Tom", 20);
P.showPerson();
}
int main()
{
test01();
system("pause");
return 0;
}
总结:类模板中成员函数类外实现时,需要加上模板参数列表。
学习目标:掌握类模板成员函数分文件编写产生的问题以及解决方式。
问题:类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到。
解决:
.hpp文件
#pragma once
#include
using namespace std;
#include
template
class Person
{
public:
//构造函数类内声明
Person(T1 name, T2 age);
void showPerson();
T1 m_Name;
T2 m_Age;
};
//构造函数类外实现
template
Person::Person(T1 name, T2 age) //模板一般加简尖括号,中为模板的参数模板
{
this->m_Name = name;
this->m_Age = age;
}
//成员函数类外实现
template
void Person::showPerson()
{
cout << "姓名:" << this->m_Name << " 年龄:" << this->m_Age << endl;
}
.cpp文件
//类模板分文件编写问题以及解决
//#include
//第一种解决方式,直接包含源文件_不常用
//#include "person.cpp"
//第二种解决方式,将.h和.cpp中的内容写到一起,将后缀名改为.hpp文件
#include "person.hpp"
//template
//class Person
//{
//public:
// //成员函数类内声明
// Person(T1 name, T2 age);
//
// void showPerson();
//
// T1 m_Name;
// T2 m_Age;
//};
//
构造函数类外实现
//template
// Person::Person(T1 name, T2 age) //模板一般加简尖括号,中为模板的参数模板
//{
// this->m_Name = name;
// this->m_Age = age;
//}
//
成员函数类外实现
//template
//void Person::showPerson()
//{
// cout << "姓名:" << this->m_Name << " 年龄:"<m_Age << endl;
//}
void test01()
{
Personp("Jerry", 18);
p.showPerson();
}
int main()
{
test01();
system("pause");
return 0;
}
总结:主流的解决方式是第二种,将类模板成员函数(声明+实现)写到一起,并将后缀名改为.hpp。
学习目标:掌握类模板配合友元函数的类内和类外实现。
全局函数类内实现——直接在类内声明有友元即可。
全局函数类外实现——需要提前让编译器知道全局函数的存在。
//1.3.8通过全局函数 打印Person信息
#include
//提前让编译器知道Person类存在
//2、全局函数配合友元 类外实现——先做函数声明,下方在做函数模板定义,在做友元
//函数模板定义
template
class Person;//函数
//类外实现
//如果声明了函数模板,可以将实现写到后面,否则需要将实现体写到类的前面让编译器提前看到
template
void printPerson2(Person p)
{
cout << "类外实现——姓名:" << p.m_Name << " 年龄:" << p.m_Age << endl;
}
template
class Person
{
//全局函数 类内实现 让其作为本类的好朋友可以访问类中的私有属性
friend void printPerson(Person p)
{
cout << "姓名:" << p.m_Name << " 年龄:" << p.m_Age << endl;
}
//全局函数 类外实现
//friend void printPerson2(Person p);
//上面为普通函数的声明、下面为函数模板的实现方式,因此导致test02链接错误
//改为下面方式:加一个空模板的参数列表
//如果全局函数 是类外实现,需要让编译器提前知道这个函数的存在
friend void printPerson2<>(Person 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()
{
Personp("Tom", 20);
printPerson(p);
}
//2、全局函数在类外实现
void test02()
{
Personp("Jerry",20);
printPerson2(p);
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
总结:建议全局函数做类内实现,用法简单,而且编译器可以直接识别。
这个案例就是写一个功能加强版的存储器。
需求分析:因为不知道具体的数据类型,将其抽象为类型T
案例分两个文件编写,MyArray.hpp文件中主要写类声明和函数的实现,main.cpp中写主程序,对功能测试。
MyArray.hpp
//自己的通用的数组类
#pragma once
#include
using namespace std;
template
class MyArray
{
public:
//有参构造 参数 容量
MyArray(int capacity)
{
//cout << "MyArray有参构造调用" << endl;
this->m_Capacity = capacity;
this->m_Size = 0;
this->pAddress = new T[this->m_Capacity];
}
//拷贝构造
MyArray(const MyArray& arr)
{
//cout << "MyArray拷贝构造调用" << endl;
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];
}
}
//opertor=防止浅拷贝问题 (连等号操作)
MyArray& operator=(const MyArray& arr)
{
//cout << "MyArray等号调用" << endl;
//先判断原来堆区石否有数据,如果有先释放
if (this->pAddress != NULL)
{
delete[]this->pAddress;
this->pAddress = NULL;
this->m_Capacity = 0;
this->m_Size = 0;
}
//深拷贝
this->m_Capacity = arr.m_Capacity;
this->m_Size = arr.m_Size;
this->pAddress = new T[arr.m_Capacity];
for (int i = 0; i < this->m_Size; i++)
{
this->pAddress[i] = arr.pAddress[i];
}
return *this;
}
//尾插法
void Push_Back(const T & val)
{
//判断容量是否等于大小
if (this->m_Capacity == this->m_Size)
{
return;
}
this->pAddress[this->m_Size] = val;//在数组末尾插入数据:将val插到下标为m_Size的位置处
this->m_Size++;//更新数组大小
}
//尾删法
void Pop_Back()
{
//让用户访问不到最后一个元素,即为尾删,逻辑删除
if (this->m_Size == 0)
{
return;
}
this->m_Size--;
}
//通过下标的方式访问数组中的元素
T& operator[](int index)
{
return this->pAddress[index];//函数调用作为左值需要返回引用
}
//返回数组容量
int getCapacity()
{
return this->m_Capacity;
}
//返回数组大小
int getSize()
{
return this->m_Size;
}
//析构函数
~MyArray()
{
if (this->pAddress != NULL)
{
//cout << "MyArray析构调用" << endl;
delete[] this->pAddress;
this->pAddress = NULL;
}
}
private:
T* pAddress;//指针指向堆区开辟的真实数组
int m_Capacity;//数组容量
int m_Size;//数组大小
};
mian.cpp
#include
using namespace std;
#include "MyArray.hpp"
#include
void printIntArray(MyArray& arr)
{
for (int i = 0; i < arr.getSize(); i++)
{
cout << arr[i] << endl;
}
}
void test01()
{
MyArrayarr1(5);
for (int i = 0; i < 5; i++)
{
//利用尾插法向数组中插入数据
arr1.Push_Back(i);
}
cout << "arr1的打印输出为:" << endl;
printIntArray(arr1); //0 1 2 3 4 5
cout << "arr1的容量为:" << arr1.getCapacity() << endl;
cout << "arr1的大小为:" << arr1.getSize() << endl;
MyArrayarr2(arr1);
cout << "arr2的打印输出:" << endl;
printIntArray(arr2);
//尾删
arr2.Pop_Back();
cout << "arr2尾删后:" << endl;
cout << "arr2的容量为:" << arr2.getCapacity() << endl;
cout << "arr2的大小为:" << arr2.getSize() << endl;
测试代码
//MyArrayarr1(5);//构造与析构
//MyArrayarr2(arr1);//拷贝构造与析构
//MyArrayarr3(100);//等号重载
//arr3 = arr1;
}
//测试自定义的数据类型
class Person
{
public:
Person() {};
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
string m_Name;
int m_Age;
};
void printPersonArray(MyArray& arr)
{
for (int i = 0; i < arr.getSize(); i++)
{
cout << "姓名:" << arr[i].m_Name << "年龄:" << arr[i].m_Age << endl;
}
}
void test02()
{
MyArrayarr(10);
Person p1("孙悟空", 999);
Person p2("韩信", 20);
Person p3("妲己", 10);
Person p4("赵云", 38);
Person p5("安其拉", 22);
//将数据插入到数组中
arr.Push_Back(p1);
arr.Push_Back(p2);
arr.Push_Back(p3);
arr.Push_Back(p4);
arr.Push_Back(p5);
//打印数组
printPersonArray(arr);
//输出容量
cout << "arr容量为:" << arr.getCapacity() << endl;
//输出大小
cout << "arr大小为:" << arr.getSize() << endl;
}
int main()
{
test01();
//test02();
system("pause");
return 0;
}
测试:
测试主要分两部分,一个为内置类型int数组的测试,一个为自定义类型Person类的测试,其结果分别如下:
总结:利用所学知识实现通用的数组。
面向对象三大特性:
封装:将类似的属性和行为抽象,封装起来为一类,从而实现事或物。
继承:子类继承父类中的属性和行为,不用重新声明,提高代码复用性。
多态:一个函数名有多个接口,父类指针指向子类对象指向同一个接口会产生不同的形态。
泛型编程:主要是模板,模板将类型参数化,使代码更具有通用性。
在开发过程中,例如写一个数字相加的算法,有人写为Add()、pulas(),名称不同,但功能相同,为了避免重复,系统自己给出,从而诞生了STL。
STL大体分为六大组件,分别为:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器。
容器:置物之所也
STL容器就是将运用最广泛的一些数据结构实现出来。
常用的数据结构:数组,链表,树,栈,队列,集合,映射表等。
这些容器分为序列式容器和关联式容器两种:
序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置。
关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系。
算法:问题之解法也
有限的步骤,解决逻辑或数学上的问题,这一门学科我们叫做算法(Algorithms)。
算法分为质变算法和非质变算法:
质变算法:是指运算过程中会更改区间内的元素的内容。例如拷贝,替换,删除等。
非质变算法:是指算法过程中不会更改区间内的元素内容,例如查找、技术、遍历、寻找极值等。
迭代器:容器和算法之间粘合剂
提供一种方法,使之能够依序寻访某个容器搜含的各个元素,而又无需暴露该容器的内部表示方式。
每个容器都有自己专属的迭代器。
迭代器使用非常类似于指针,初学阶段可以先理解迭代器为指针。
迭代器种类:
了解STL种容器、算法、迭代器概念之后,我们利用代码感受STL的魅力
STL中最常常用的容器为vector,可以理解为数组,下面将学习如何向这个容器中插入数据、并遍历这个容器。
容器:vector
算法:for_each
迭代器:vector
实例:
#include
#include //标准算法头文件
//vector容器存放内置数据类型
void myPrint(int val)
{
cout << val << endl;
}
void test01()
{
//创建了一个vector容器对象(数组),并且通过模板参数指定容器中存放的数据类型
vector v;
//向容器种插入数据
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
通过迭代器访问容器中的数据
//vector::iterator itBegin = v.begin();//起始迭代器 指向容器中的第一个元素
//vector::iterator itEnd = v.end();//结束迭代器 指向容器中的最后一个元素的下一个位置
第一种遍历方式
//while (itBegin != itEnd)//等于时退出循环
//{
// cout << *itBegin << endl;
// itBegin++;
//}
第二种遍历方式(简单常用)
//for (vector::iterator it = v.begin(); it != v.end();it++)
//{
// cout << *it << endl;
//}
//第三种遍历方式 利用STL提供遍历方式 头文件algorithm
for_each(v.begin(), v.end(), myPrint);
//利用回调技术,在for_each遍历期间调用函数
}
int main()
{
test01();
system("pause");
return 0;
}
学习目标:vector中存放自定义数据类型,并打印输出。
//2.5.2 vector中存放自定义数据类型
#include
#include
//自定义数据类型
class Person
{
public:
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
string m_Name;
int m_Age;
};
//存放对象
void test01()
{
vector v;
//创建数据
Person p1("aaa", 10);
Person p2("bbb", 20);
Person p3("ccc", 30);
Person p4("ddd", 40);
Person p5("eee", 50);
//向容器中插入数据
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
v.push_back(p5);
//遍历容器中的数据
for (vector::iterator it = v.begin(); it != v.end(); it++)
{
//两种获取类中属性方式
//cout << "姓名:" << (*it).m_Name << " 年龄:" << (*it).m_Age << endl;
cout << "姓名:" << it->m_Name << " 年龄:" << it->m_Age << endl;
}
//it 为一个指针,(*it)中的类型为尖括号<>中的类型,.运算拿到具体属性。
}
//存放自定义数据类型 指针
//存放对象指针
void test02()
{
vector v;
Person p1("aaa", 10);
Person p2("bbb", 20);
Person p3("ccc", 30);
Person p4("ddd", 40);
Person p5("eee", 50);
//向容器中插入数据
v.push_back(&p1);
v.push_back(&p2);
v.push_back(&p3);
v.push_back(&p4);
v.push_back(&p5);
//遍历容器中的数据
for (vector::iterator it = v.begin(); it != v.end(); it++)
{
//cout << "姓名:" << (*it)->m_Name << " 年龄:" << (*it)->m_Age << endl;//*it为Person*类型的数据(指针)
Person* p = (*it);
cout << "::姓名:" << p->m_Name << " 年龄:" << p->m_Age << endl;
}
//it 为一个指针,(*it)中的类型为尖括号<>中的类型,.运算拿到具体属性。
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
学习目标:容器中嵌套容器,将所有数据进行遍历输出。
//2.5.3 vector容器嵌套容器
#include
//容器嵌套容器
void test01()
{
vector> v;
//创建小容器
vector v1;
vector v2;
vector v3;
vector 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);
}
//将小容器插入到大容器中
v.push_back(v1);
v.push_back(v2);
v.push_back(v3);
v.push_back(v4);
//通过大容器,把所有数据遍历一遍
for (vector>::iterator it = v.begin(); it != v.end(); it++)
{
//(*it)——容器vector,所以还需要一个遍历读出数据
for (vector::iterator vit = (*it).begin(); vit != (*it).end(); vit++)
{
cout << *vit << " ";
}
cout << endl;
}
}
int main()
{
test01();
system("pause");
return 0;
}
本质:
string和char*区别:
特点:
string类内部封装了很多成员方法。
例如:查找find,拷贝copy,删除delete,替换replace,插入insert。
string管理char*所分配的内存,不用担心复制越界和取值越界,有类内部进行负责。
构造函数原型:
示例:
//3.1.2 string的构造函数
#include
void test01()
{
string s1;//默认构造
const char* str = "helllo world";
string s2(str);
cout << "s2= " << s2 << endl;
string s3(s2);
cout << "s3= " << s3 << endl;
string s4(10, 'a');
cout << "s4= " << s4 << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
功能描述:给string字符串进行赋值。
赋值的函数原型:(大致分为两种:等号赋值,assign()赋值)
示例:
//3.1.3 string 赋值操作
#include
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(10, 'w');
cout << "str7= " << str7 << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
总结:string的赋值有很多,operator=这种方式是比较实用的。
但是要记住assign这种方式,别人用的时候要认识。
功能描述:实现字符串末尾拼接字符串。
函数原型:
示例:
//3.1.4 strng字符串拼接
#include
void test01()
{
string str1 = "我";
str1 += "爱玩游戏";
cout << "str1= " << str1 << endl;//我爱玩游戏
str1 += ':';
cout << "str1= " << str1 << endl;//我爱玩游戏:
string str2 = "LOL DNF";
str1 += str2;
cout << "str1= " << str1 << endl;//我爱玩游戏:LOL DNF
string str3 = "I";
str3 += " LOVE ";
cout << "str3= " << str3 << endl;//I LOVE
str3.append("game abcde", 4);
cout << "str3= " << str3 <
总结:字符串拼接重载版本很多,初学阶段记住几种即可。
功能描述:查找:查找指定字符串是否存在;
替换:再指定的位置替换字符串。
函数原型:
示例:
//3.1.5 string字符串查找和替换
#include
//1、查找
void test01()
{
//find
string str1 = "abcdefgde";
int pos=str1.find("de");
if (pos == -1)
{
cout << "未找到字符串" << endl;
}
else
{
cout << "找到字符串,pso= " << pos << endl;//3
}
//rfind和find的区别
//rfind从右往左查找 find从左往右查找
pos = str1.rfind("de");
cout << "pso= " << pos << endl;//7
}
//2、替换
void test02()
{
string str1 = "abcdefg";
str1.replace(1, 3, "1111");//从1号位置起3个字符替换为“1111”
cout << "str1= " << str1 << endl;//a1111efg
}
int main()
{
//test01();
test02();
system("pause");
return 0;
}
总结:
功能描述:字符串之间的比较。
比较方式:字符串比较时按字符的ASCII码进行对比。= 返回 0;> 返回 1;
< 返回 -1。
函数原型:
示例:
//3.1.6 字符串比较
void test01()
{
string str1 = "hello";
string str2 = "hello";
if (str1.compare(str2) == 0)
{
cout << "str1 等于 str2" << endl;
}
else
{
cout << "str1 不等于 str2" << endl;
}
}
int main()
{
test01();
system("pause");
return 0;
}
总结:字符串对比主要是用于比较两个字符串是否相等,判断谁大谁小的意义并不是很大。
string中单个字符存取方式有两种:
示例:
//3.1.7 string 字符存取
#include
void test01()
{
string str = "hello world";
//通过[]访问单个字符
for (int i = 0; i < str.size(); i++)
{
cout << str[i] << " ";//hello world
}
cout << endl;
//通过at方法访问单个字符
for (int i = 0; i < str.size(); i++)
{
cout << str.at(i) << " ";//hello world
}
cout << endl;
//修改单个字符
str[0] = 'x';
cout << "str= " << str << endl;//xello world
str[1] = 'x';
cout << "str= " << str << endl;//xxllo world
}
int main()
{
test01();
system("pause");
return 0;
}
总结:string字符串中单个字符存取有两种方式,利用[]或者at.
功能描述:对string字符串进行插入和删除操作。
函数原型:
示例:
//3.1.8string字符串插入和删除
#include
void test01()
{
string str = "hello";
//插入
/*string str2 = "1112";
str.insert(1, str2);*/
str.insert(1, "111");
cout << "str= " << str << endl;//h111ello
//删除
str.erase(1, 3);
cout << "str= " << str << endl;//hello
}
int main()
{
test01();
system("pause");
return 0;
}
总结:插入和删除的起始下标都是从0开始。
功能描述:从字符串中获取想要的子串。
函数原型:
示例:
//3.1.9 string子串
void test01()
{
string str = "abcdf";
string subStr = str.substr(1, 3);
cout << "subStr= " << subStr << endl;//bcd
}
//使用操作
void test02()
{
string email = "[email protected]";
//从邮件地址中 获取用户名信息
int pos=email.find("@");//8
//cout << pos << endl;
string usrName = email.substr(0, pos);
cout << usrName << endl;//zhangsan
}
int main()
{
//test01();
test02();
system("pause");
return 0;
}
总结:灵活的运用求解子串功能,可以在实际开发中获取有效信息。
功能:vector数据结构和数组非常相似,也称为单端数组。
vector与普通数组区别:不同之处在于数组是静态空间,而vector可以动态扩展。
动态扩展:
功能描述:创建vector容器。
函数原型:
示例:
//3.2.2 vector容器的构造
#include
void printVector(vector& v)
{
for (vector::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
vector v1;//默认无参构造
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
printVector(v1);
vectorv2(v1.begin(), v1.end());//通过区间方式进行构造
printVector(v2);
vectorv3(10, 100);//n个elem方式
printVector(v3);
vectorv4(v3);//拷贝构造
printVector(v4);
}
int main()
{
test01();
system("pause");
return 0;
}
总结:vector的多种构造方式没有可比性,灵活使用即可。
功能描述:给vector容器进行赋值。
函数原型:
示例:
//3.2.3vector的赋值
#include
void printVector(vector& v)
{
for (vector::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
vectorv1;//无参构造
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
printVector(v1);//0 1 2 3 4 5 6 7 8 9
//赋值- operator
vectorv2;
v2 = v1;
printVector(v2);//0 1 2 3 4 5 6 7 8 9
//赋值- assign
vectorv3;
v3.assign(v1.begin(), v1.end());//前闭后开
printVector(v3);//0 1 2 3 4 5 6 7 8 9
//赋值- n个elem方式
vectorv4;//构造
v4.assign(10, 100);//赋值行为
printVector(v4);//100 100 100 100 100 100 100 100 100 100
}
int main()
{
test01();
system("pause");
return 0;
}
总结:vector赋值方式比较简单,使用operator=,或者assign都可以。
功能描述:对vector容器的容量和大小操作。
函数原型:
示例:
//3.2.4 vector容量和大小
#include
void printVector(vector& v)
{
for (vector::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
vectorv1;
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;//13
cout << "v1的大小为:" << v1.size() << endl;//10
}
//重新指定大小
v1.resize(15);//扩充到15个
//v1.resize(15,100);//利用重载的版本,可以指定默认填充值为参数2
printVector(v1);//如果重新指定的比原来的长,默认用0填充新的位置
v1.resize(5);
printVector(v1);//如果重新指定的比原来的短了,超出部分会删除掉
}
int main()
{
test01();
system("pause");
return 0;
}
总结:判断为空:empty(); 返回元素个数:size();
返回容器容量:capacity(); 重新指定大小:resize()。
功能描述:对vector容器进行插入、删除操作。
函数原型:
示例:
//vector插入和删除
#include
void printVector(vector& v)
{
for (vector::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
vectorv1;
//尾插
v1.push_back(10);
v1.push_back(20);
v1.push_back(30);
v1.push_back(40);
v1.push_back(50);
printVector(v1);//10 20 30 40 50
//尾删
v1.pop_back();
printVector(v1);//10 20 30 40
//插入 第一个参数是迭代器
v1.insert(v1.begin(), 100);
printVector(v1);//100 10 20 30 40
v1.insert(v1.begin(), 2, 1000);
printVector(v1);//1000 1000 10 20 30 40
//删除 参数也是迭代器
v1.erase(v1.begin());//1000 10 20 30 40
printVector(v1);
//清空
//v1.erase(v1.begin(), v1.end());
v1.clear();
printVector(v1);
}
int main()
{
test01();
system("pause");
return 0;
}
总结:尾插:push_back; 尾删:pop_back; 插入:insert(位置迭代器);
删除:erase(位置迭代器); 清空:clear。
功能描述:对vector中的数据的存取操作。
函数原型:
示例:
//3.2.6 vector容器数据存取
#include
void test01()
{
vectorv1;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
//利用[]方式访问数组中元素
for (int i = 0; i < 10; i++)
{
cout << v1[i] << " ";
}
cout << endl;//0 1 2 3 4 5 6 7 8 9
//利用at方式访问元素
for (int i = 0; i < 10; i++)
{
cout << v1.at(i) <<" ";
}
cout<
总结:
功能描述:实现两个容器内元素进行互换。
函数原型:swap(vec); //将vec与本身的元素互换。
示例:
//3.2.7 vector互换容器
#include
void printVector(vector& v)
{
for (vector::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
//1、基本使用
void test01()
{
vectorv1;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
cout << "交换前:" << endl;
printVector(v1);
vectorv2;
for (int i = 10; i > 0; i--)
{
v2.push_back(i);
}
printVector(v2);
cout << "交换后:" << endl;
v1.swap(v2);//互换
printVector(v1);
printVector(v2);
}
//2、实际使用
//巧用swap可以收缩内存空间
void test02()
{
vectorv;
for (int i = 0; i < 100000; i++)
{
v.push_back(i);
}
cout << "v的容量为:" << v.capacity() << endl;//138255
cout << "v的大小为: " << v.size() << endl;//100000
v.resize(3);//重设大小为3
cout << "v的容量为:" << v.capacity() << endl;// 138255(太浪费)
cout << "v的大小为: " << v.size() << endl;//3
//巧用swap收缩内存
vector(v).swap(v);//匿名对象
cout << "v的容量为:" << v.capacity() << endl;// 3
cout << "v的大小为: " << v.size() << endl;//3
}
int main()
{
test01();
//test02();
system("pause");
return 0;
}
test01:
test02:
总结:swap可以使两个容器互换,可以达到实用的收缩内存效果。
功能描述:减少vector在动态扩展容量时的扩展次数。
函数原型:reserve(int len); //容器预留len个元素长度,预留位置不初始化,元素不可访问。
// 3.2.8 vector预留空间
#include
void test01()
{
vectorv;
//利用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;
//不预留30(动态扩展30次) 预留为1
}
int main()
{
test01();
system("pause");
return 0;
}
总结:如果数据量较大,可以一开始利用reserve预留空间。
功能:双端数组,可以对头端进行插入删除操作。
deque与vector区别:
deque内部工作原理:
deque内部有个中控器,维护每段缓冲区中的内容,缓冲区存放真实数据。
中控器维护的时每个缓冲区的地址,使得使用deque时像一片连续的内存空间。
deque容器的迭代器也是支持随机访问的。
功能描述:deque容器构造。
函数原理:
示例:
//deque容器构造函数
#include
void printDeque(const deque& d)
{
for (deque::const_iterator it = d.begin(); it != d.end(); it++)
{
//*it = 100;//修改值,为了不能修改,加const
cout << *it << " ";
}
cout << endl;
}
void test01()
{
dequed1;//无参构造
for (int i = 0; i < 10; i++)
{
d1.push_back(i);
}
printDeque(d1);
dequed2(d1.begin(), d1.end());
printDeque(d2);
dequed3(10,100);
printDeque(d3);
dequed4(d3);
printDeque(d4);
}
int main()
{
test01();
system("pause");
return 0;
}
总结:
限定容器数据为只读时,遍历数据的迭代器也要设置为只读;
deque容器和vector容器的构造方式几乎一样,灵活使用即可。
功能描述:给deque容器进行赋值。
函数原型:
//3.3.2 deque赋值操作
#include
void printDeque(const deque& d)
{
for (deque::const_iterator it = d.begin(); it != d.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
dequed1;
for (int i = 0; i < 10; i++)
{
d1.push_back(i);
}
printDeque(d1);
//operator=赋值
dequed2;
d2 = d1;
printDeque(d2);
//asign 赋值
dequed3;
d3.assign(d1.begin(), d1.end());
printDeque(d3);
dequed4;
d4.assign(10, 100);
printDeque(d4);
}
int main()
{
test01();
system("pause");
return 0;
}
功能描述:对deque容器的大小进行操作。
函数原型:
//3.3.4 deque大小操作
#include
void printDeque(const deque& d)
{
for (deque::const_iterator it = d.begin(); it != d.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
dequed1;
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;//10
//deque容器没有容量概念
}
//重新指定大小
//d1.resize(15);
d1.resize(15, 1);//用1填充
printDeque(d1);
d1.resize(5);
printDeque(d1);//0 1 2 3 4 5
}
int main()
{
test01();
system("pause");
return 0;
}
总结:
功能描述:像deque容器中插入和删除数据。
函数原型:
示例:
//3.3.5 deque插入和删除
#include
void printDeque(const dequed)
{
for (deque::const_iterator it = d.begin(); it != d.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
//两端操作
void test01()
{
dequed1;
//尾插
d1.push_back(10);
d1.push_back(20);
//尾插
d1.push_front(100);
d1.push_front(200);
printDeque(d1);//200 100 10 20
//尾删
d1.pop_back();
printDeque(d1);//200 100 10
//头删
d1.pop_front();
printDeque(d1);//100 10
}
void test02()
{
dequed1;
d1.push_back(10);
d1.push_back(20);
d1.push_back(100);
d1.push_back(200);
printDeque(d1);//200 100 10 20
//insert插入
d1.insert(d1.begin(), 1000);
printDeque(d1);//1000 200 100 10 20
d1.insert(d1.begin(), 2, 10000);
printDeque(d1);//10000 10000 1000 200 100 10 20
//按照区间进行插入
dequed2;
d2.push_back(1);
d2.push_back(2);
d2.push_back(3);
d1.insert(d1.begin(), d2.begin(), d2.end());
printDeque(d1);//1 2 3 10000 10000 1000 200 100 10 20
}
void test03()
{
dequed1;
d1.push_back(10);
d1.push_back(20);
d1.push_front(100);
d1.push_front(200);
//删除
deque::iterator it = d1.begin();
it++;
d1.erase(it);
printDeque(d1);// 200 10 20
//按区间方式删除
//d1.erase(d1.begin(), d1.end());//全删
//清空
d1.clear();
printDeque(d1);
}
int main()
{
test01();
//test02();
//test03();
system("pause");
return 0;
}
总结 :
功能描述:对deque中的数据的存取操作。
函数原型:
//3.3.6 deque容器数据存取
#include
void test01()
{
dequed;
d.push_back(20);
d.push_back(30);
d.push_front(100);
d.push_front(200);
d.push_front(300);//300 200 100 20 30
//通过[]凡是访问元素
for (int i = 0; i < d.size(); i++)
{
cout << d[i] << " ";
}
cout << endl;
//通过at的凡是访问元素
for (int i = 0; i < d.size(); i++)
{
cout << d.at(i) << " ";
}
cout << endl;
cout << "第一个元素为:" << d.front() << endl;//300
cout << "最后一个元素为:" << d.back() << endl;//30
}
int main()
{
test01();
system("pause");
return 0;
}
总结:
功能描述:利用算法实现读deque容器进行排序。
算法:sort(iterator beg,iterator end) //读beg和end区间内元素进行排序。
//3.3.7 deque排序
#include
#include//标准算法的头文件
void printDeque(const deque&d)
{
for (deque::const_iterator it = d.begin(); it != d.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
dequed;
d.push_back(10);
d.push_back(20);
d.push_front(100);
d.push_front(200);
d.push_front(300);//300 200 100 10 20
printDeque(d);
//排序 默认排序规则:从小到大 升序
//对于支持随机访问的迭代器的容器,都可以利用sort算法直接对其进行排序
//vector容器也是可以利用sort进行排序
sort(d.begin(),d.end());
cout << "排序后:" << endl;
printDeque(d);//10 20 100 200 300
}
int main()
{
test01();
system("pause");
return 0;
}
总结:sort算法非常使用,使用时包含头文件algorithm即可。
案例描述:有5名选手:ABCDE,10个评委分别对每一名选手打分,去除评委中最高分,最低分,取平均分。
实现步骤:
// 3.4 案例——评委打分
#include
#include
#include
#include
#include
//选手类
class Person
{
public:
Person(string name, int score)
{
this->m_Name = name;
this->m_Score = score;
}
string m_Name;//姓名
int m_Score;//平均分
};
void creatPerson(vector& v)
{
string nameSeed = "ABCDE";
for (int i = 0; i < 5; i++)
{
string name = "选手";
name += nameSeed[i];
int score = 0;
Person p(name, score);
//将创建的person对象 放入到容器中
v.push_back(p);
}
}
//打分
void setScore(vector& v)
{
for (vector::iterator it = v.begin(); it != v.end(); it++)
{
//将评委的分数 放入到deque容器中
dequed;
for (int i = 0; i < 10; i++)
{
int score = rand() % 41 + 60;//60~100
d.push_back(score);
}
测试——输出具体打分
//cout << "选手:" << it->m_Name << "打分:" << endl;
//for (deque::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::iterator dit = d.begin(); dit != d.end(); dit++)
{
sum += *dit; //累加每个评委的分数
}
int avg = sum / d.size();
//将平均分 赋值给选手身上
it->m_Score = avg;
}
}
//输出最后得分
void showScore(vector& v)
{
for (vector::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名选手
vectorv;//存放选手容器
creatPerson(v);
测试
//for (vector::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;
}
概念:stack是一种先进后出(First In Last Out,FILO)的数据结构,它只有一个出口。
栈不允许有遍历的行为,只能访问栈顶元素。
栈中只有顶端的元素才可以被外界使用,因此栈不允许有遍历的行为。
栈中进入数据称为——入栈(push)
栈中弹出数据称为——出栈(pop)
功能描述:栈容器常用的对外接口。
#include
//栈stack容器
void test01()
{
//特点:符合先进后出的数据结构
stacks;
//入栈
s.push(10);
s.push(20);
s.push(30);
s.push(40);
//如果栈不为空,查看栈顶,并且执行出栈操作
while (!s.empty())
{
//查看栈顶元素
cout << "栈顶元素为:" << s.top() << endl;
//出栈
s.pop();
}
cout << "栈的大小: " << s.size()<
概念:queue是一种先进先出(First In First Out,FIFO)的数据结构,它有两个出口。
队列容器允许从一端新增元素,从另一端移除元素。
队列中只有队头和队尾采可以被外界使用,因此队列不允许有遍历行为。
队列中进数据称为——入队 push.
队列中出数据称为——出队 pop.
功能描述:栈容器常用的对外接口。
示例:
#include
class Person
{
public:
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
string m_Name;
int m_Age;
};
void test01()
{
//创建队列
queueq;
//准备数据
Person p1("唐僧", 30);
Person p2("孙悟空", 1000);
Person p3("猪八戒", 900);
Person p4("沙僧", 800);
//入队
q.push(p1);
q.push(p2);
q.push(p3);
q.push(p4);
//判断只要队列不为空,查看对头,查看队尾,出队
//队列不提供迭代器,更不支持随机访问
while (!q.empty())
{
//查看队头
cout << "队头元素——姓名:" << q.front().m_Name << "年龄:" << q.front().m_Age << endl;
//查看队尾
cout << "队尾元素——姓名:" << q.back().m_Name << "年龄:" << q.back().m_Age << endl;
//出队
q.pop();
}
cout << "队列大小为:"<
//3.7.2 List构造函数
#include
void printList(const list& L)
{
for (list::const_iterator it = L.begin(); it != L.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
listL1;//默认构造
//添加数据
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
printList(L1);
listL2(L1.begin(), L1.end());//区间方式构造
printList(L2);
listL3(L2);//拷贝构造
printList(L3);
listL4(10, 1000);//n个elem
printList(L4);
}
int main()
{
test01();
system("pause");
return 0;
}
//3.7.3 list容器赋值和交换
#include
void printList(const list& L)
{
for (list::const_iterator it = L.begin(); it != L.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
//赋值
void test01()
{
listL1;//默认构造
//添加数据
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
printList(L1);
listL2;
L2 = L1;//operator=赋值
printList(L2);
listL3;
L3.assign(L2.begin(), L2.end());
printList(L3);
listL4;
L4.assign(10, 1000);
printList(L3);
}
//交换
void test02()
{
listL1;//默认构造
//添加数据
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
listL2;
L2.assign(10,100);
cout << "交换前:" << endl;
printList(L1);
printList(L2);
L1.swap(L2);
cout << "交换后:" << endl;
printList(L1);
printList(L2);
}
int main()
{
//test01();
test02();
system("pause");
return 0;
}
//3.7.4 list大小操作
#include
void printList(const list& L)
{
for (list::const_iterator it = L.begin(); it != L.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
listL1;
//添加数据
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
printList(L1);
//判断容器是否为空
if (L1.empty())
{
cout << "L1为空" << endl;
}
else
{
cout << "L1不为空" << endl;
cout << "L1的元素个数为:" << L1.size() << endl;
}
//重新指定大小
L1.resize(10);//用0来填充 10 20 30 40 0 0 0 0 0 0
printList(L1);
L1.resize(2);//10 20
printList(L1);
}
int main()
{
test01();
system("pause");
return 0;
}
//3.7.5 List容器插入和删除
#include
void printList(const list& L)
{
for (list::const_iterator it = L.begin(); it != L.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
listL;
//尾插
L.push_back(10);
L.push_back(20);
L.push_back(30);
//头插
L.push_front(100);
L.push_front(200);
L.push_front(300);
printList(L);//300 200 100 10 20 30
//尾删
L.pop_back();
printList(L);//300 200 100 10 20
//头删
L.pop_front();
printList(L);//200 100 10 20
//insert插入
//L.insert(L.begin(), 1000);
//printList(L);//1000 200 100 10 20
//还可以这样插入
list::iterator it = L.begin();
L.insert(++it, 1000);
printList(L); //200 1000 100 10 20
//删除
it = L.begin();
L.erase(++it);
printList(L);//200 100 10 20
//移除
L.push_back(10000);
L.push_back(10000);
printList(L);//200 100 10 20 10000 10000
L.remove(10000);
printList(L);//200 100 10 20
//清空
L.clear();
printList(L);
}
int main()
{
test01();
system("pause");
return 0;
}
插入和删除需要提供迭代器。
/3.7.6 list数据存取
#include
void printList(const list& L)
{
for (list::const_iterator it = L.begin(); it != L.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
listL1;
//尾插
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(30);
//L1[0];不可以用[]访问list容器中的元素
//L1.at(0); 不可以用at访问list容器中的元素
/st本质是一个链表,不是用连续线性空间存储数据,迭代器也是不支持随机访问的
cout << "第一个元素为:" << L1.front() << endl;
cout << "最后一个元素为:" << L1.back() << endl;
//验证迭代器是不支持随机访问的
list::iterator it = L1.begin();
//it = it + 1;//错误,不支持随机访问
it++;//正确,支持双向
it--;
}
int main()
{
test01();
system("pause");
return 0;
}
//3.7.7 list容器的反转和排序
#include
void printList(const list& L)
{
for (list::const_iterator it = L.begin(); it != L.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
listL1;
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
cout << "反转前:" << endl;
printList(L1);//10 20 30 40
//反转
L1.reverse();
cout << "反转后:" << endl;
printList(L1);//40 30 20 10
}
//回调函数
bool myCompare(int v1, int v2)
{
//降序 就让第一个数>第二个数
return v1 > v2;
}
void test02()
{
listL1;
L1.push_back(20);
L1.push_back(10);
L1.push_back(40);
L1.push_back(30);
//排序
cout << "排序前:" << endl;
printList(L1);
//所有不支持随机访问迭代器的容器,不可以用标准算法
//不支持随机访问迭代器的容器,内部会提供对应一些算法
//sort(L1.begin(), L1.end());
//sort是成员函数
L1.sort();//默认排序规则 从小到大 升序
cout << "排序后:" << endl;
printList(L1);
L1.sort(myCompare);//降序
printList(L1);
}
int main()
{
//test01();
test02();
system("pause");
return 0;
}
//3.7.8 排序案例
#include
#include
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()
{
listL;//创建容器
//准备数据
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::iterator it = L.begin(); it != L.end(); it++)
{
cout << "姓名:" << (*it).m_Name << " 年龄:" << it->m_Age << " 身高:" << it->m_Height << endl;
}
//排序
cout << "___________________" << endl;
cout << "排序后:" << endl;
//L.sort();//错误
L.sort(comparePerson);
for (list::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;
}
//3.8.2 set构造和赋值
#include
/t容器遍历
void printSet(const set& s)
{
for (set::const_iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
sets1;
//插入数据 只有insert的方式
s1.insert(10);
s1.insert(40);
s1.insert(30);
s1.insert(20);
s1.insert(30);
//t容器特点:所有元素再插入时会自动排序
//t容器不允许插入重复的值
printSet(s1);//10 20 30 40
//拷贝构造
sets2(s1);
printSet(s2);//10 20 30 40
//赋值
sets3;
s3 = s2;
printSet(s3);//10 20 30 40
}
int main()
{
test01();
system("pause");
return 0;
}
//3.8.3 set大小和交换
#include
void printSet(const set& s)
{
for (set::const_iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
sets1;
//插入数据
s1.insert(10);
s1.insert(40);
s1.insert(30);
s1.insert(20);
s1.insert(30);
printSet(s1);//10 20 30 40
//判断是否为空
if (s1.empty())
{
cout << "s1为空" << endl;
}
else
{
cout << "s1不为空" << endl;
//t不支持resize,重新指定大小
cout << "s1的大小为:" << s1.size() << endl;//4
}
}
//交换
void test02()
{
sets1;
//插入数据
s1.insert(10);
s1.insert(40);
s1.insert(30);
s1.insert(20);
sets2;
//插入数据
s2.insert(100);
s2.insert(400);
s2.insert(300);
s2.insert(200);
cout << "交换前:" << endl;
printSet(s1);//10 20 30 40
printSet(s2);//100 200 300 400
cout << "交换后:" << endl;
s1.swap(s2);
printSet(s1);//100 200 300 400
printSet(s2);//10 20 30 40
}
int main()
{
//test01();
test02();
system("pause");
return 0;
}
/3.8.4 set容器插入和删除
#include
void printSet(set& s)
{
for (set::iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
sets1;
//插入数据
s1.insert(10);
s1.insert(40);
s1.insert(30);
s1.insert(20);
printSet(s1);//10 20 30 40
//删除
s1.erase(s1.begin());
printSet(s1);//20 30 40
//删除重载版本
s1.erase(30);
printSet(s1);//20 40
//清空
//s1.erase(s1.begin(), s1.end());
s1.clear();
printSet(s1);
}
int main()
{
test01();
system("pause");
return 0;
}
//3.8.5 set查找和统计
#include
void test01()
{
sets1;
//插入数据
s1.insert(10);
s1.insert(40);
s1.insert(30);
s1.insert(20);
s1.insert(30);
s1.insert(30);//set中存储:10 20 30 40
//查找
set::iterator pos = s1.find(30);//返回的是迭代器
if (pos != s1.end())
{
cout << "找到了元素:" << *pos << endl;
}
else
{
cout << "未找到元素" << endl;
}
//统计
int num = s1.count(30);
cout << "num=" << num << endl;//1
}
int main()
{
test01();
system("pause");
return 0;
}
//3.8.6 set和multiset区别
#include
void test01()
{
sets;
pair::iterator, bool> ret = s.insert(10);
//对于set来所,返回值有个bool类型的检测,插入成功或者失败有个返回
if (ret.second)//为真插入成功
{
cout << "第一次插入成功" << endl;//成功
}
else
{
cout << "第一次插入失败" << endl;
}
ret = s.insert(10);
if (ret.second)//为真插入成功
{
cout << "第二次插入成功" << endl;
}
else
{
cout << "第二次插入失败" << endl;//失败
}
multisetms;//允许插入重复的值
//而multiset只是返回个迭代器
ms.insert(10);
ms.insert(10);
ms.insert(10);
for (multiset::iterator it = ms.begin(); it!= ms.end(); it++)
{
cout << *it << " ";//10 10 10
}
cout << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
//3.8.7 pair队组创建
#include
#include
void test01()
{
pairp(string("Tom"), 20);
cout << "姓名:" << p.first << " 年龄:" << p.second << endl;
pairp2 = make_pair("Jerry", 10);
cout << "姓名:" << p2.first << " 年龄:" << p2.second << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
示例1:存放内置数据类型
//3.8.8 set容器排序
//1、内置类型指定排序规则
#include
class MyCompare
{
public:
bool operator()(int v1, int v2)const
{
return v1 > v2;//降序
}
};
//1、内置类型指定排序规则
void test01()
{
sets1;
s1.insert(10);
s1.insert(40);
s1.insert(20);
s1.insert(30);
for (set::iterator it = s1.begin(); it != s1.end(); it++)
{
cout << *it << " ";
}
cout << endl;//10 20 30 40
//指定排序规则为从大到小——利用仿函数
sets2;//按照仿函数规则排序
s2.insert(10);
s2.insert(40);
s2.insert(20);
s2.insert(30);
s2.insert(20);
s2.insert(40);
for (set::iterator it = s2.begin(); it != s2.end(); it++)
{
cout << *it << " ";
}
cout << endl;//40 30 20 10
}
int main()
{
test01();
system("pause");
return 0;
}
总结:利用仿函数可以指定set容器的排序规则。
示例2:set存放自定义数据类型
/3.8.8 set容器排序
//1、自定义类型指定排序规则
#include
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()
{
//自定义数据类型 要指定排序规则
sets;
//创建Person对象
Person p1("刘备", 24);
Person p2("关羽", 28);
Person p3("张飞", 25);
Person p4("赵云", 21);
s.insert(p1);
s.insert(p2);
s.insert(p3);
s.insert(p4);
for (set::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.1 map容器构造和赋值
#include
总结:map容器中所有元素都是成对出现,插入数据时候要用对组。
//3.9.2 map大小和交换
#include
//3.9.3 map插入和删除
#include
//3.9.5 map查找和统计
#include
//3.9.6 map容器排序
//利用仿函数改变排序规则
#include
//3.10 员工分组案例
#include
#include
// 4.函数对象(仿函数)
//4.1.2 函数对象使用
#include
class MyAdd
{
public:
int operator()(int v1, int v2)
{
return v1 + v2;
}
};
//1、函数对象在使用时,可以像普通函数那样调用,可以有参数,可以有返回值
void test01()
{
MyAdd myAdd;//实际上为一个类创建的对象
cout << myAdd(10, 10) << endl;
}
//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
//普通函数可能只能拿全局变量或者静态变量来记录
}
//3、函数对象可以作为参数传递
void doPrint(MyPrint& mp, string test)
{
mp(test);
}
void test03()
{
MyPrint myPrint;
doPrint(myPrint, "Hello c++");//Hello c++
}
int main()
{
//test01();
//test02();
test03();
system("pause");
return 0;
}
总结:仿函数写法非常灵活,可以作为参数进行传递。
//4.2.2 一元谓词
#include
#include
class GreaterFive
{
public:
//一元谓词
bool operator()(int val)
{
return val > 5;
}
};
void test01()
{
vectorv;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
//查找容器中 有没有大于5的数字
//GreaterFive()匿名函数对象
vector::iterator it=find_if(v.begin(), v.end(), GreaterFive());
if (it == v.end())
{
cout << "未找到" << endl;
}
else
{
cout << "找到了大于5的数字为:" << *it << endl;//6
}
}
int main()
{
test01();
system("pause");
return 0;
}
//4.2.3 二元谓词
#include
#include
class MyCompare
{
public:
bool operator()(int val1, int val2)
{
return val1 > val2;
}
};
void test01()
{
vectorv;
v.push_back(10);
v.push_back(40);
v.push_back(20);
v.push_back(30);
v.push_back(30);
sort(v.begin(), v.end());
for (vector::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;//10 20 30 30 40
//使用函数对象 改变算法策略,变为排序规则从大到小
sort(v.begin(), v.end(), MyCompare());
cout << "________" << endl;
for (vector::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";//40 30 30 20 10
}
cout << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
总结:参数只有两个的谓词,称为二元谓词。
//4.2.3 算术仿函数
#include//内建函数对象头文件
//negate——一元仿函数,取反仿函数
void test01()
{
negaten;
cout << n(50) << endl;//-50
}
//plus 二元仿函数 加法
void test02()
{
plusp;
cout << p(10, 20) << endl;//30
}
int main()
{
//test01();
test02();
system("pause");
return 0;
}
总结:使用内建函数对象时,需要引入头文件#include
//4.3.3 关系仿函数
#include
#include
#include
class MyCompare
{
public:
bool operator()(int val1, int val2)
{
return val1 > val2;
}
};
void test01()
{
vectorv;
v.push_back(10);
v.push_back(30);
v.push_back(40);
v.push_back(20);
v.push_back(50);
for (vector::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;//10 20 30 40 50
//降序
//sort需要引用头文件#include
//sort(v.begin(), v.end(), MyCompare());
//使用内建函数 需要引用头文件 #include 其内部构造就类似于class MyCompare
sort(v.begin(), v.end(), greater());//greater大于
for (vector::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;//50 40 30 20 10
}
int main()
{
test01();
system("pause");
return 0;
}
总结:关系仿函数中最常用的就是greater<>大于。
//4.3.4 逻辑仿函数
//逻辑非 logical_not
#include
#include
#include
void test01()
{
vectorv;
v.push_back(true);
v.push_back(false);
v.push_back(true);
v.push_back(false);
for (vector::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;//1 0 1 0
//利用逻辑非 将容器v 搬运到 容器2中,并执行取反操作
vectorv2;
v2.resize(v.size());//必须提前开辟空间
transform(v.begin(), v.end(), v2.begin(),logical_not());//transform中的参数:元容器的起始 终止,目标容器的起始,仿函数
for (vector::iterator it = v2.begin(); it != v2.end(); it++)
{
cout << *it << " ";
}
cout << endl;//0 1 0 1
}
int main()
{
test01();
system("pause");
return 0;
}
总结:逻辑仿函数实际应用中用的较少,了解即可。
//5.1 常用遍历算法
//5.1.1 for_each
#include
#include
//普通函数
void print01(int val)
{
cout << val << " ";
}
//仿函数
class print02
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
void test01()
{
vectorv;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
//遍历
//普通函数将函数名字放入第三个参数位置
for_each(v.begin(), v.end(), print01);//0 1 2 3 4 5 6 7 8 9
cout << endl;
//仿函数将函数对象放入第三个参数位置 print02()为匿名函数对象
for_each(v.begin(), v.end(), print02());
cout << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
总结:for_each在实际开发中最常用遍历算法,需要熟练掌握。
//5.1.2 transform
#include
#include
class Transform
{
public:
int operator()(int v)
{
return v+100;//搬运中还可以做逻辑上的运算
}
};
class MyPrint
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
void test01()
{
vectorv;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
vectorvTarget;//目标容器
//开辟目标容器空间_一定要注意提前开辟
vTarget.resize(v.size());
transform(v.begin(), v.end(), vTarget.begin(), Transform());//将v搬运到vTarget
for_each(vTarget.begin(), vTarget.end(), MyPrint());
cout << endl;// 100 101 102 103 104 105 106 107 108 109
}
int main()
{
test01();
system("pause");
return 0;
}
总结:搬运的目标容器必须提前开辟空间,否则无法正常搬运
//5.2 常用查找算法
//5.2.1 find
#include
#include
#include
//1、内置数据类型查找
void test01()
{
vectorv;
for (int i = 0; i < 10; i++)
{
v.push_back(i + 1);
}
//查找容器中是否有5 这个元素
vector::iterator it = find(v.begin(), v.end(),5);
if (it == v.end())
{
cout << "没有找到!" << endl;
}
else
{
cout << "找到了" << *it << endl;
}
}
//2、自定义数据类型查找
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;
}
string m_Name;
int m_Age;
};
void test02()
{
vectorv;
//创建数据
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);
Person pp("bbb", 20);
vector::iterator it = find(v.begin(), v.end(), pp);
if (it == v.end())
{
cout << "没有找到!" << endl;
}
else
{
cout << "找到姓名:" << it->m_Name << " 年龄: " << it->m_Age << endl;
}
}
int main()
{
//test01();
test02();
system("pause");
return 0;
}
//5.2.2 find_if
#include
#include
#include
//1、内置数据类型
//谓词
class GreaterFive
{
public:
bool operator()(int val)
{
return val > 5;
}
};
void test01()
{
vectorv;
for (int i = 0; i < 10; i++)
{
v.push_back(i + 1);
}
//查找容器中是否有5 这个元素
vector::iterator it = find_if(v.begin(), v.end(), GreaterFive());
if (it == v.end())
{
cout << "没有找到!" << endl;
}
else
{
cout << "找到了" << *it << endl;// 6
}
}
//2、自定义数据类型
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;
}
string m_Name;
int m_Age;
};
//谓词
class Greater20
{
public:
bool operator()(Person& p)
{
return p.m_Age > 20;
}
};
void test02()
{
vectorv;
//创建数据
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::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;
}
//5.2.3 adjacent_find
#include
#include
void test01()
{
vectorv;
v.push_back(0);
v.push_back(2);
v.push_back(0);
v.push_back(4);
v.push_back(1);
v.push_back(3);
v.push_back(3);
vector::iterator pos=adjacent_find(v.begin(), v.end());
if (pos == v.end())
{
cout << "没有找到相邻重复元素!" <
总结:面试题中如果出现查找相邻重复元素,记得用STL中的adjacent_find算法。
//5.2.4 binary_search
#include
#include
void test01()
{
vectorv;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
//查找容器中是否有9
//注意:容器必须时有序的序列
bool ret = binary_search(v.begin(), v.end(),9);
if (ret)
{
cout << "找到了" << endl;
}
else
{
cout << "未找到" << endl;
}
}
int main()
{
test01();
system("pause");
return 0;
}
总结:二分查找法查找效率很高,值得注意的是查找的容器中元素必须为有序序列。
//5.2.5 count
#include
#include
#include
//1、统计内置数据类型
void test01()
{
vectorv;
v.push_back(10);
v.push_back(40);
v.push_back(30);
v.push_back(40);
v.push_back(20);
v.push_back(40);
int num = count(v.begin(), v.end(),40);
cout << "40的元素个数为:" << num << endl;//3
}
//2、统计自定义数据类型
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;
}
return false;
}
string m_Name;
int m_Age;
};
void test02()
{
vectorv;
Person p1("刘备", 35);
Person p2("关羽", 35);
Person p3("张飞", 35);
Person p4("曹操", 45);
Person p5("赵云", 40);
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;//3
}
int main()
{
//test01();
test02();
system("pause");
return 0;
}
总结:统计自定义数据类型时候,需要配合重载operator==。
//5.2.6 count_if
#include
#include
#include
//1、统计内置数据类型
//谓词
class Greater20
{
public:
bool operator()(int val)
{
return val > 20;
}
};
void test01()
{
vectorv;
v.push_back(10);
v.push_back(40);
v.push_back(30);
v.push_back(40);
v.push_back(20);
v.push_back(40);
//查找容器中是否有5 这个元素
int num = count_if(v.begin(), v.end(), Greater20());
cout << "大于20的个数为:" << num <m_Name = name;
this->m_Age = age;
}
string m_Name;
int m_Age;
};
//谓词
class AgeGreater20
{
public:
bool operator()(const Person& p)
{
return p.m_Age > 20;
}
};
void test02()
{
vectorv;
//创建数据
Person p1("刘备", 35);
Person p2("关羽", 35);
Person p3("张飞", 35);
Person p4("曹操", 45);
Person p5("赵云", 20);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
v.push_back(p5);
//统计年龄大于20的个数
int num= count_if(v.begin(), v.end(), AgeGreater20());
cout << "大于20岁的人员个数为:" << num << endl;//4
}
int main()
{
//test01();
test02();
system("pause");
return 0;
}
//5.3 常用排序算法
//5.3.1 sort
#include //sort
#include
#include //greater
void myPrint(int val)
{
cout << val << " ";
}
void test01()
{
vectorv;
v.push_back(10);
v.push_back(50);
v.push_back(30);
v.push_back(20);
v.push_back(40);
//sort默认从小到大排序
sort(v.begin(), v.end());
for_each(v.begin(), v.end(), myPrint);
cout << endl;//10 20 30 40 50
//从大到小
sort(v.begin(), v.end(),greater());
for_each(v.begin(), v.end(), myPrint);
cout << endl;//50 40 30 20 10
}
int main()
{
test01();
system("pause");
return 0;
}
总结:sort属于开发中最长用的算法之一,需熟练掌握。
//5.3.2 random_shuffle
#include //sort
#include
#include //greater
#include//time
void myPrint(int val)
{
cout << val << " ";
}
void test01()
{
srand((unsigned int)time(NULL));
vectorv;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
//打乱顺序
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 merge
#include //sort
#include
#include //greater
void myPrint(int val)
{
cout << val << " ";
}
void test01()
{
vectorv1;
vectorv2;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
v2.push_back(i+1);
}
//目标容器
vectorvTarget;
//提前给目标容器分配空间
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;
}
总结:merge合并的两个容器必须有序序列。
//5.3.4 reverse
#include //sort
#include
#include //greater
void myPrint(int val)
{
cout << val << " ";
}
void test01()
{
vectorv;
v.push_back(10);
v.push_back(30);
v.push_back(50);
v.push_back(20);
v.push_back(40);
cout << "反转前:"<
总结:reverse反转区间元素,面试题可能涉及到。
//5.4 常用拷贝和替换算法
//5.4.1 copy
#include
#include
#include
void myPrint(int val)
{
cout << val << " ";
}
void test01()
{
vectorv1;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
vectorv2;
v2.resize(v1.size());
//不要过分依赖,实际开发中有=赋值拷贝直接用更方便
copy(v1.begin(), v1.end(), v2.begin());
for_each(v2.begin(), v2.end(), myPrint);
cout << endl;//0~9
}
int main()
{
test01();
system("pause");
return 0;
}
总结:利用copy算法在拷贝时,目标容器记得提前开辟空间。
//5.4.2 replace
#include //for_each
#include
#include
//仿函数
class MyPrint
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
void test01()
{
vectorv;
v.push_back(20);
v.push_back(30);
v.push_back(50);
v.push_back(20);
v.push_back(20);
v.push_back(30);
v.push_back(40);
cout << "替换前:" << endl;
for_each(v.begin(), v.end(), MyPrint());
cout << endl;
//将所有20替换为2000
replace(v.begin(), v.end(), 20, 2000);
cout << "替换后:" << endl;
for_each(v.begin(), v.end(), MyPrint());
cout << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
总结:replace会替换区间内满足条件的元素。
//5.4.3 replace_if
#include //for_each
#include
#include
//仿函数
class MyPrint
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
//利用仿函数筛选出满足条件的数
class Greater30
{
public:
bool operator()(int val)
{
return val >= 30;
}
};
void test01()
{
vectorv;
v.push_back(20);
v.push_back(30);
v.push_back(50);
v.push_back(20);
v.push_back(20);
v.push_back(30);
v.push_back(40);
cout << "替换前:" << endl;
for_each(v.begin(), v.end(), MyPrint());
cout << endl;
//将大于等于30 替换为 3000
replace_if(v.begin(), v.end(), Greater30(), 3000);
cout << "替换后:" << endl;
for_each(v.begin(), v.end(), MyPrint());
cout << endl;//20 3000 3000 20 20 3000 3000
}
int main()
{
test01();
system("pause");
return 0;
}
总结:replace_if按条件查询,可以利用仿函数灵活筛选满足的条件。
//5.4.3 replace_if
#include //for_each
#include
#include
//仿函数
class MyPrint
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
//利用仿函数筛选出满足条件的数
class Greater30
{
public:
bool operator()(int val)
{
return val >= 30;
}
};
void test01()
{
vectorv;
v.push_back(20);
v.push_back(30);
v.push_back(50);
v.push_back(20);
v.push_back(20);
v.push_back(30);
v.push_back(40);
cout << "替换前:" << endl;
for_each(v.begin(), v.end(), MyPrint());
cout << endl;
//将大于等于30 替换为 3000
replace_if(v.begin(), v.end(), Greater30(), 3000);
cout << "替换后:" << endl;
for_each(v.begin(), v.end(), MyPrint());
cout << endl;//20 3000 3000 20 20 3000 3000
}
int main()
{
test01();
system("pause");
return 0;
}
总结:swap交换容器时,注意交换的容器要同种类型。
总结:accumulate使用时头文件注意时numeric,这个算法很实用。
//5.5.2 fill
#include
#include
#include
void myPrint(int val)
{
cout << val << " ";
}
void test01()
{
vectorv;
v.resize(10);//10个0
//后期重新填充
fill(v.begin(), v.end(), 100);
for_each(v.begin(), v.end(), myPrint);
cout << endl;//100 100 100 100 100 100 100 100 100 100
}
int main()
{
test01();
system("pause");
return 0;
}
总结:利用fill可以将容器区间内元素填充为指定的值。
//5.6.1 set_intersection(交集)
#include
#include
void myPrint(int val)
{
cout << val << " ";
}
void test01()
{
vectorv1;
vectorv2;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);//0~9
v1.push_back(i+5);//5~14
}
vectorvTarget;
//目标容器提前开辟空间
//最特殊的情况:大容器包含小容器,开辟空间 取小容器的size即可
vTarget.resize(min(v1.size(), v2.size()));
//获取交集 两个序列必须有序 返回目标容器的最后一个元素的迭代器地址
vector::iterator itEnd=set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());
for_each(vTarget.begin(), itEnd, myPrint);
//结束迭代器若为vTarget.end(); 则输出为 5 6 7 8 9 0 0 0 0
cout << endl;//5 6 7 8 9
}
int main()
{
test01();
system("pause");
return 0;
}
总结:求交集的两个集合必须的有序序列。
//5.6.2 set_union(并集)
#include
#include
void myPrint(int val)
{
cout << val << " ";
}
void test01()
{
vectorv1;
vectorv2;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);//0~9
v1.push_back(i + 5);//5~14
}
vectorvTarget;
//目标容器提前开辟空间
//最特殊的情况:两个容器没有交集 并集就是两个容器size相加
vTarget.resize(v1.size()+v2.size());
vector::iterator itEnd = set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());
for_each(vTarget.begin(), itEnd, myPrint);
//结束迭代器若为vTarget.end(); //0~14 0 0 0 0 0
cout << endl;//0~14
}
int main()
{
test01();
system("pause");
return 0;
}
//5.6.3 set_difference(差集)
#include
#include
void myPrint(int val)
{
cout << val << " ";
}
void test01()
{
vectorv1;
vectorv2;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);//0~9
v1.push_back(i + 5);//5~14
}
vectorvTarget;
//给目标容器开辟空间
//特殊情况:两个集合相互独立没有交集,就为大容器size大小为目标容器空间
vTarget.resize(max(v1.size(), v2.size()));
cout << "v1和v2的差集:" << endl;
vector::iterator itEnd = set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());
for_each(vTarget.begin(), itEnd, myPrint);
//结束迭代器若为vTarget.end(); //0 1 2 3 4 0 0 0 0 0
cout << endl;//0 1 2 3 4
cout << "v2和v1的差集:" << endl;
itEnd = set_difference(v2.begin(), v2.end(), v1.begin(), v1.end(), vTarget.begin());
for_each(vTarget.begin(), itEnd, myPrint);
//结束迭代器若为vTarget.end();
cout << endl;// 10 11 12 13 14
}
int main()
{
test01();
system("pause");
return 0;
}