C++提高编程
1. 模板
1.1 模板的概念
- 建立通用的模板,大大提高复用性
1.2 函数模板
- C++的另一种编程思想称为泛型编程,主要利用的技术就是模板
- C++提供两种模板机制:函数模板和类模板
1.2.1 函数模板语法
函数模板的作用:建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表
语法:template<typename T>
template:声明创建模板
typename:表明其后面的符号是一种数据类型,可用class代替
T:通用的数据类型,名称可以替换,通常为大写字母
1.2.2 函数模板注意事项
- 自动类型推导,必须推导出一致的数据类型T才可使用
- 模板必须要确定出T的数据类型,才可以使用
//函数模板
template<typename T>//声明一个模板,告诉编译器后面代码的T是一个通用数据类型,不要报错
void my_swap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
void test2()
{
int a = 10, b = 20;
double c = 1.2, d = 2.3;
char e = 'e';
my_swap(a, b);//自动类型推导
my_swap<double>(c, d);//显示指定类型
//my_swap(d, e);//错误,推导不出一致的数据类型
cout << "a=" << a << " b=" << b << " c=" << c << " d=" << d << endl;
}
1.2.3 普通函数和函数模板的区别
- 普通函数调用时可以发生自动类型转换(隐式类型转换)
- 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
- 如果利用显示指定类型的方式,可以发生隐式类型转换
#include<iostream>
using namespace std;
//普通函数
int my_add1(int a, int b)
{
return a + b;
}
//函数模板
template<class T>
T my_add2(T a, T b)
{
return a + b;
}
void test1()
{
int a = 10;
int b = 20;
char c = 'c';
cout << my_add1(a, b) << endl;
cout << my_add1(a, c) << endl;//c的ASCII码是99,调用该函数的时候隐式类型转换了
cout << my_add2(a, b) << endl;//自动类型推导
//cout << my_add2(a, c) << endl;//自动类型推导无法隐式类型转换
cout << my_add2<int>(a, c) << endl;//显示指定类型即可发生隐式类型转换
}
int main()
{
test1();
system("pause");
return 0;
}
1.2.4 普通函数与模板函数的调用规则
- 如果函数模板和普通函数都可以实现,优先调用普通函数
- 可以通过空模板参数列表来强制调用函数模板
- 函数模板也可以发生重载
- 如果函数模板可以产生更好的匹配,优先调用函数模板
#include<iostream>
using namespace std;
void my_print(int a, int b)
{
cout << "调用的普通函数" << endl;
}
template<class T>
void my_print(T a, T b)
{
cout << "调用的函数模板" << endl;
}
template<class T>
void my_print(T a, T b, T c)
{
cout << "调用的重载函数模板" << endl;
}
void test1()
{
int a = 10;
int b = 20;
my_print(a, b);//若都可调用,则优先调用普通函数
my_print<>(a, b);//通过空模板参数列表强制调用函数模板
my_print(a, b, 100);
char c1 = 'a', c2 = 'b';
my_print(c1, c2);//如果函数模板可以产生更好的匹配(不用隐式类型转换),则调用函数模板
}
int main()
{
test1();
system("pause");
return 0;
}
- 总结:既然提供了函数模板,就最好不要使用普通函数,否则容易出现二义性
1.2.5 模板的局限性
- 模板并不是万能的,有些具体数据类型,要用具体化的方式做特殊实现
- 总结:利用具体化的模板,可以解决自定义类型的通用化,学习模板并不是为了写模板,而是在STL能够运用系统提供的模板
1.3 类模板
1.3.1 类模板语法
建立一个通用类,类中的成员数据类型可以不具体制定,用一个虚拟的类型来代表
语法:template<typename T>
template:声明创建模板
typename:表明其后面的符号是一种数据类型,可用class代替
T:通用的数据类型,名称可以替换,通常为大写字母
1.3.2 类模板和函数模板的区别
- 类模板没有自动类型推导的使用方式
- 类模板在模板参数列表中可以有默认参数
//类模板
template<class NameType,class AgeType = int>//可以有默认参数列表(默认age是int型)
class person
{
public:
person(NameType name, AgeType age)
{
m_name = name;
m_age = age;
}
void show_person()
{
cout << "name=" << m_name << " age=" << m_age << endl;
}
NameType m_name;
AgeType m_age;
};
void test1()
{
//person p0("fuck", 17);//错误,类模板不能自动类型推导
person<string, int> p1("fuck", 14);//实例化对象的时候要指定模板参数列表的数据类型
person<string> p2("cufk", 15);//可以省略默认参数
p1.show_person();
}
1.3.3 类模板中成员函数创建时机
类模板中成员函数和普通类中成员函数创建时机是有区别的:
- 普通类中的成员函数一开始就可以创建
- 类模板中的成员函数在调用时才创建
//普通类
class person1
{
public:
void show_person1()
{
cout << "person1 show" << endl;
}
};
class person2
{
public:
void show_person2()
{
cout << "person2 show" << endl;
}
};
//类模板
template<class T>
class my_class
{
public:
T obj;
//类模板中的成员函数只有在调用的时候才会创建
void func1()
{
obj.show_person1();
}
void func2()
{
obj.show_person2();
}
};
void test1()
{
my_class<person1> m;//确定了obj是person1类型,则m可以调用fun1
m.func1();
//m.func2();//错误,函数调用时才去创建成员函数,而m不能调用fun2
my_class<person2> n;
n.func2();
}
1.3.4 类模板对象做函数参数
类模板实例化出的对象,向函数传参的方式:
- 指定传入的类型:直接显示对象的数据类型
- 参数模板化:将对象中的参数变为模板进行传递
- 整个类模板化:将这个对象类型模板化进行传递
#include<iostream>
#include<string>
using namespace std;
template<class T1,class T2>
class person
{
public:
person(T1 name, T2 age)
{
m_name = name;
m_age = age;
}
void show_person()
{
cout << "name=" << m_name << " age=" << m_age << endl;
}
T1 m_name;
T2 m_age;
};
//指定传入类型
void print_person1(person<string, int>& p)
{
p.show_person();
}
void test1()
{
person<string, int> p("fuck", 15);
print_person1(p);
}
//参数模板化
template<class T1,class T2>
void print_person2(person<T1,T2>& p)
{
p.show_person();
cout << "T1的类型为:" << typeid(T1).name() << endl;
cout << "T2的类型为:" << typeid(T2).name() << endl;
}
void test2()
{
person<string, int> p("trae", 15);
print_person2(p);
}
//整个类模板化
template<class T>
void print_person3(T& p)
{
p.show_person();
cout << "T的类型为:" << typeid(T).name() << endl;
}
void test3()
{
person<string, int> p("young", 21);
print_person3(p);
}
int main()
{
test1();
test2();
test3();
system("pause");
return 0;
}
1.3.5 类模板与继承
- 当子类继承的父类是一个类模板时,子类在声明的时候要指定出父类中T的类型
- 如果不指定,编译器无法给子类分配内存
- 如果想灵活指定出父类中T的类型,子类也要变为类模板
1.3.6 类模板成员函数类外实现
- 需要加上模板参数列表
template<class T1,class T2>
class person
{
public:
person(T1 name, T2 age);
/*{
m_name = name;
m_age = age;
}*/
void show_person();
/*{
cout << "name=" << m_name << " age=" << m_age << endl;
}*/
T1 m_name;
T2 m_age;
};
//构造函数类外实现
template<class T1,class T2>
person<T1,T2>::person(T1 name, T2 age)//类模板的构造函数实现,要加模板参数列表
{
m_name = name;
m_age = age;
}
//成员函数类外实现
template<class T1,class T2>
void person<T1, T2>::show_person()
{
cout << "name=" << m_name << " age=" << m_age << endl;
}
void test1()
{
person<string, int> p("fuck", 22);
p.show_person();
}
1.3.7 类模板分文件编写
问题:类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到
解决方法:
- 直接包含.cpp源文件
- 将声明和实现写到同一个文件中,并更改后缀名为.hpp
1.3.8 类模板与友元
全局函数类内实现:直接在类内声明友元即可
全局函数类外实现:需要提前让编译器知道全局函数的存在
2. STL初识
2.1 STL的诞生
- C++的面向对象和泛型编程思想,目的就是复用性的提升
- 为建立数据结构和算法的一套标准,诞生了STL
2.2 STL的基本概念
- STL(Standard Template Library)标准模板库
- STL从广义上分:容器、算法、迭代器
- 容器和算法之间通过迭代器无缝连接
- STL几乎所有的代码都采用了模板类或者模板函数
2.3 STL六大组件
STL六大组件为:容器、算法、迭代器、仿函数、适配器、空间配置器
- 容器:各种数据结构,如vector,list,deque,set,map等,用来存放数据
- 算法:各种常用的算法,如sort,find,copy,for_each等
- 迭代器:容器与算法之间的胶合剂
- 仿函数:行为类似函数,可作为算法的某种策略
- 适配器:一种用来修饰容器或者仿函数或者迭代器接口的东西
- 空间配置器:负责空间的配置与管理
2.4 STL中的容器、算法、迭代器
容器
STL容器就是将运用最广泛的一些数据结构实现出来
序列式容器:强调值的排序,每个元素均有固定的位置
- vector、list、deque、stack、queue、heap、priority_queue、slist
关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系
- RB-tree、set、map、multiset、multimap、hashtable、hash_set、hash_map、hash_multiset、hash_multimap
算法
- 质变算法:运算过程中会更改区间内的元素的内容,例如拷贝、替换、删除等
- 非质变算法:运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等
迭代器
容器和算法之间的胶合剂
提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式,每个容器都有自己专属的迭代器,迭代器的使用非常类似指针
2.5 容器算法迭代器初识
STL中最常用的容器为vector,可以理解为数组
- 通过迭代器访问容器中的元素
vector<int>::iterator it_begin() = v.begin(); //起始迭代器,指向容器中第一个元素
vector<int>::iterator it_end = v.end();//结束迭代器,指向容器中最后一个元素的下一个位置
//第一种遍历方式
while (it_begin != it_end)
{
cout << *it_begin << " ";
it_begin++;
}
cout << endl;
//第二种遍历方式
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
void my_print(int val)
{
cout << val<<" ";
}
//第三种遍历方式
for_each(v.begin(), v.end(), my_print); // 使用STL提供的for_each标准遍历算法
3. STL常用容器
3.1 string容器
3.1.1 string基本概念
本质:
- string是C++风格的字符串,而string本质上是一个类
string和char*的区别:
- char*是一个指针
- string是一个类,类内部封装了char*,管理这个字符串,是一个char*的容器
特点:
- string类内部封装了很多成员方法
- 查找find,拷贝copy,删除delete,替换replace,插入insert
- string管理char*所分配的内存,不用担心复制越界和取值越界等,由类内部进行负责
3.1.2 string构造函数和析构函数
string();
:生成一个空字符串s,例如string str;
string(const char* s);
:使字符串s初始化string(const string& str);
:拷贝构造string(int n,char c);
:使用n个字符c初始化
void test1()
{
string s1;//默认构造
const char* str = "fuck trae young";
string s2(str);
cout << "s2=" << s2 << endl;
string s3(s2);
cout << "s3=" << s3 << endl;
string s4(10, 'k');
cout << "s4=" << s4 << endl;
}
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 test1()
{
string str1;
str1 = "fuck trae young";
cout << "str1=" << str1 << endl;
string str2;
str2 = str1;
cout << "str2=" << str2 << endl;
string str3;
str3 = 'a';
cout << "str3=" << str3 << endl;
string str4;
str4.assign("fuck you");
cout << "str4=" << str4 << endl;
string str5;
str5.assign("fuck you", 4);
cout << "str5=" << str5 << endl;
string str6;
str6.assign(str4);
cout << "str6=" << str6 << endl;
string str7;
str7.assign(10, 'f');
cout << "str7=" << str7 << endl;
}
3.1.4 string字符串拼接
拼接函数原型:
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);
:同operator+=(const string& str)string& append(const string& s,int pos,int n);
:字符串中从pos开始的n个字符连接到字符串结尾
void test1()
{
string str1 = "我";
str1 += "朝那面打";
cout << "str1=" << str1 << endl;
str1 += ';';
cout << "str1=" << str1 << endl;
string str2 = "打呗";
str1 += str2;
cout << "str1=" << str1 << endl;
string str3 = "芜湖";
str3.append("~");
cout << "str3=" << str3 << endl;
str3.append("game avd", 4);
cout << "str3=" << str3 << endl;
//str3.append(str1);
//cout << "str3=" << str3 << endl;
str3.append(str1, 0, 3);
cout << "str3=" << str3 << endl;
}
3.1.5 string查找和替换
总结:
- find是从左往右找,rfind是从右往左找
- find找到字符串后返回查找的第一个字符串位置,找不到则返回-1
- replace在替换时,要指定从哪个位置起,多少个字符替换为什么样的字符串
//查找
void test1()
{
string str1 = "fuck trae young trae";
int pos = str1.find("tra");//找到tra在str1中第一次出现位置的索引,未找到则pos=-1
cout << "pos=" << pos << endl;
//rfind是从右往左找第一次出现的位置,find是从左往右
pos = str1.rfind("tra");
cout << "pos=" << pos << endl;
}
//替换
void test2()
{
string str1 = "abcdefg";
str1.replace(1, 3, "1111");//从第一个位置开始后面三个字符替换为1111
cout << "str1=" << str1 << endl;
}
3.1.6 string字符串比较
比较方式:
- 按字符的ASCII码进行比较
=
=
=返回0
>
>
>返回1
<
<
<返回-1
函数原型:
int compare(const string &s) const;
:与字符串s比较int compare(const char* s) const;
:与字符串s比较
//比较
void test3()
{
string str1 = "fuck";
string str2 = "guck";
if (str1.compare(str2) == 0)
{
cout << "str1等于str2" << endl;
}
else if (str1.compare(str2) > 0)
{
cout << "str1大于str2" << endl;
}
else
{
cout << "str1小于str2" << endl;
}
}
3.1.7string字符存取
char& operator[](int n);
:通过[]取字符char& at(int n);
:通过at取字符
//存取
void test4()
{
string str = "fuck trae young";
//通过[]访问单个字符
//str.size()返回字符串长度(或str.length())
for (int i = 0; i < str.size(); i++)
{
cout << str[i] << " ";
}
cout << endl;
//通过at访问单个字符
for (int i = 0; i < str.size(); i++)
{
cout << str.at(i) << " ";
}
cout << endl;
//修改单个字符
str[0] = 'g';
str.at(1) = 'i';
cout << "str=" << str << endl;
}
3.1.8 string插入和删除
函数原型:
string& insert(int pos, const char* s);
:在pos位置之后插入字符串string& insert(int pos, const string& str);
:在pos位置之后插入字符串string& insert(int pos, int n, char c);
:在pos位置插入n个字符cstring& erase(int pos, int n);
:删除pos开始的n个字符
//插入和删除
void test5()
{
string str1 = "hello";
str1.insert(1, "111");//在第一个位置插入111
cout << "str1=" << str1 << endl;
str1.erase(1, 3);//在第一个位置往后删3个字符
cout << "str1=" << str1 << endl;
}
3.1.9 string子串
函数原型:
string substr(int pos=0,int n=npos) const;
:返回从pos开始的n个字符组成的字符串
//求子串
void test6()
{
string str = "abcdef";
string sub = str.substr(1, 3);//从第一个位置开始往后数3个位置的子串
cout << "sub=" << sub << endl;
string email = "1328861924@qq.com";
int pos = email.find("@");
cout << "qq=" << email.substr(0, pos) << endl;
}
3.2 vector容器
3.2.1 vector基本概念
功能:
- vector数据结构跟数组非常类似,也称为单端数组
vector与普通数组的区别:
- 不同之处在于数组是静态空间,而vector可以动态扩展
动态扩展:
- 并不是在原有空间之后接续新空间,而是找更大的空间,然后将原数据拷贝到新空间,释放原空间
- vector的迭代器是支持随机访问的迭代器
3.2.2 vector构造函数
void print_vector(vector<int>& v)
{
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
vector<int> v1;//默认构造
//通过区间方式进行构造
vector<int> v2(v1.begin(), v1.end());
//n个elem方式构造
vector<int> v3(10, 100);//10个100
vector<vector<int>> v4(2,vector<int>(2,0));//2个长度为2值为0的一维数组
vector<int> v5(v3);
3.2.3 赋值操作
//赋值
void test2()
{
vector<int> v1;//默认构造
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
print_vector(v1);
//赋值
vector<int> v2;
v2 = v1;//重载operator=
print_vector(v2);
vector<int> v3;
v3.assign(v1.begin(), v1.end());//assign区间赋值
print_vector(v3);
vector<int> v4;
v4.assign(10, 100);//assign10个100
print_vector(v4);
}
3.2.4 vector容量和大小
capacity()
:容量
size()
:大小
容量
≥
\ge
≥大小
void test3()
{
vector<int> v1;//默认构造
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
print_vector(v1);
if (v1.empty())
{
cout << "v1为空" << endl;
}
else
{
cout << "v1不为空" << endl;
cout << "v1的容量为:" << v1.capacity() << endl;
cout << "v1的大小为:" << v1.size() << endl;
}
//重新指定大小
v1.resize(15);
print_vector(v1);//如果重新指定得比原来长了,多的默认添0
v1.resize(20, 20);//可用指定值取代默认值
print_vector(v1);
v1.resize(10);//比原来短了,超出的部分直接删
print_vector(v1);
}
3.2.5 vector插入和删除
//插入和删除
void test4()
{
vector<int> v1;//默认构造
v1.push_back(10);//尾插法
v1.push_back(20);
v1.push_back(30);
v1.push_back(40);
v1.push_back(50);
print_vector(v1);
v1.pop_back();//尾删法
print_vector(v1);
v1.insert(v1.begin(), 0);//在头部插入0,第一个参数是迭代器
print_vector(v1);
v1.insert(v1.begin(), 2, 100);//在头部插两个100
print_vector(v1);
v1.erase(v1.begin());//删除头部元素
print_vector(v1);
//v1.erase(v1.begin(), v1.end());//从头删到尾
v1.clear();//直接清空容器
print_vector(v1);
}
3.2.6 vector数据存取
v[i]
:重载[]访问元素
v.at(i)
:at访问元素
v.front()
:第一个元素
v.back()
:最后一个元素
3.2.7 vector互换容器
实现两个容器内元素进行互换
函数原型:v.swap(vec);
将vec与v的元素互换
3.2.8 vector预留空间
减少vector在动态扩展容量时的扩展次数
函数原型:reserve(int len);
容器预留len个元素长度,预留位置不初始化,元素不可访问
//预留空间
void test8()
{
vector<int> v;
v.reserve(10000);//利用reserve预留空间,则num=1
int num = 0;//统计内存开辟次数
int* p = NULL;
for (int i = 0; i < 10000; i++)
{
v.push_back(i);
//让p指向v的首地址,每开辟一次内存,首地址会变化,num统计开辟次数
if (p != &v[0])
{
p = &v[0];
num++;
}
}
cout << "num=" << num << endl;
}
3.3 deque容器
3.3.1 deque容器基本概念
功能:
- 双端数组,可以对头端进行插入删除操作
deque和vector的区别:
- vector对于头部的插入删除效率低,数据量越大,效率越低
- deque相对而言,对头部的插入删除速度比vector快
- vector访问元素的速度比deque快
deque迭代器也支持随机访问
3.3.2 deque构造、赋值、大小、数据存取
与vector类似
3.3.3 deque插入删除
void print_deque(const deque<int>& d)//只可读不可写,注意要用const_iterator
{
for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
//
插入删除
void test4()
{
deque<int> d1;
//尾插
d1.push_back(10);
d1.push_back(20);
//头插
d1.push_front(30);
d1.push_front(40);
print_deque(d1);
d1.pop_back();//尾删
print_deque(d1);
d1.pop_front();//头删
print_deque(d1);
d1.insert(d1.begin(), 50);//insert插入
print_deque(d1);
d1.insert(d1.begin(), 2, 60);//插2个60
print_deque(d1);
deque<int> d2;
d2.push_back(1);
d2.push_back(2);
d2.push_back(3);
d1.insert(d1.begin(), d2.begin(), d2.end());//在d1的begin插入d2的begin到d2的end的元素
print_deque(d1);
deque<int>::iterator it = d1.begin();
it++;
d1.erase(it);//删除第二个元素
print_deque(d1);
d1.erase(d1.begin(), d1.end());//按区间方式删除
d1.clear();//清空
print_deque(d1);
}
3.3.4 deque排序
sort(d.begin(),d.end());
:默认升序
3.4 stack容器
3.4.1 stack基本概念
stack是一种先进后出的数据结构,只有一个出口,栈中只有栈顶的元素可以被外界使用,因此栈不能有遍历行为
3.4.2 stack常用接口
void test1()
{
stack<int> s;
//入栈
s.push(14);
s.push(15);
s.push(16);
s.push(17);
cout << "栈的大小为:" << s.size() << endl;
//只要栈不为空,查看栈顶并出栈
while (!s.empty())
{
cout << "栈顶元素为:" << s.top() << endl;
s.pop();//出栈
}
cout << "栈的大小为:" << s.size() << endl;
}
3.5 queue容器
3.5.1 queue基本概念
queue是一种先进先出的数据结构,有两个出口,队列容器允许一端新增元素,从另一端移除元素,只有队头和队尾的元素允许被外界使用,因此队列没有遍历行为
3.5.2 queue常用接口
push(elem); //往队尾添加元素
pop();//移除队头元素
back();//返回最后一个元素
front();//返回第一个元素
empty();//判断队列是否为空
size();//队列大小
3.6 list容器
3.6.1 list基本概念
功能:将数据链式存储
链表是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的
链表的组成:链表由一系列结点组成
结点的组成:一个存储数据元素的数据域,另一个是存储下一个结点地址的指针域
STL中的链表是双向循环链表
由于链表的存储方式并不是连续的内存空间,因此链表中的迭代器只支持前移和后移,属于双向迭代器
3.6.2 list构造、赋值和大小
与vector类似
3.6.3 list数据存取
void test5()
{
list<int> L;
//尾插
L.push_back(10);
L.push_back(20);
L.push_back(30);
L.push_back(40);
//L[0]、L.at(0)都不行,list不支持随机访问
cout << "第一个元素为:" << L.front() << endl;
cout << "最后一个元素为:" << L.back() << endl;
//验证迭代器支不支持随机访问
list<int>::iterator it = L.begin();
it++;
it--;//支持双向
//it = it + 1;//不支持随机访问
}
3.6.4 list反转和排序
L.reverse();
:反转list
//sort(L.begin(), L.end());
//所有不支持随机访问迭代器的容器,都不能用标准算法
//不支持随机访问迭代器的容器,内部提供了一些算法
L.sort();//默认升序
3.7 set/multiset容器
3.7.1 set基本概念
简介:
- 所有的元素在插入时自动被排序
本质:
- set/multiset属于关联式容器,底层结构是二叉树
区别:
- set不允许容器中有重复的元素
- multiset允许容器中有重复的元素
3.7.2 set构造和赋值
//构造和赋值
void test1()
{
set<int> s1;
//插入数据只有insert方式
s1.insert(1);
s1.insert(4);
s1.insert(2);
s1.insert(3);
s1.insert(4);
s1.insert(3);
//set容器特点:所有元素在插入时自动排序且无重复值
print_set(s1);
set<int> s2(s1);//拷贝构造
print_set(s2);
set<int> s3 = s1;//赋值
print_set(s3);
}
3.7.3 set大小和交换
与vector类似
3.7.4 set插入和删除
//插入和删除
void test3()
{
set<int> s1;
s1.insert(4);
s1.insert(5);
s1.insert(2);
s1.insert(4);
s1.insert(1);
print_set(s1);
//删除
s1.erase(s1.begin());
print_set(s1);
s1.insert(6);
print_set(s1);
s1.erase(6);//把值为6的删了
print_set(s1);
//s1.erase(s1.begin(), s1.end());
s1.clear();//清空
print_set(s1);
}
3.7.5 set查找和统计
//查找和统计
void test4()
{
set<int> s1;
s1.insert(4);
s1.insert(5);
s1.insert(2);
s1.insert(4);
s1.insert(1);
print_set(s1);
set<int>::iterator pos=s1.find(2);//返回2的位置
if (pos != s1.end())
{
cout << "找到元素:" << *pos << endl;
}
else
{
cout << "未找到元素" << endl;
}
int num = s1.count(4);//统计4的个数
cout << "num=" << num << endl;//对于set而言,统计的结果0/1
}
3.7.6 set和multiset的区别
- set不可以插入重复数据,而multiset可以
- set插入数据的同时会返回插入结果,表示插入是否成功
- multiset不会检测数据,因此可以插入重复数据
3.7.7 pair对组创建
功能:
- 成对出现的数据,利用对组可以返回两个数据
两种创建方式:
pair<type,type> p(value1,value2);
pair<type,type> p=make_pair(value1,value2);
3.7.8 set容器排序
//set容器排序
class my_compare
{
public:
//重载operator()
//成员函数名后const表明这是一个常量成员函数,不能修改成员变量的值
bool operator()(int v1,int v2) const
{
return v1 > v2;
}
};
void test7()
{
set<int> s1;
s1.insert(1);
s1.insert(4);
s1.insert(2);
s1.insert(5);
s1.insert(3);
print_set(s1);
//指定排序规则为从大到小
set<int,my_compare> s2;
s2.insert(1);
s2.insert(4);
s2.insert(2);
s2.insert(5);
s2.insert(3);
//不同的容器迭代器不同
for (set<int, my_compare>::iterator it = s2.begin(); it != s2.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
//set容器排序(自定义数据类型)
class person
{
public:
person(string name, int age)
{
m_name = name;
m_age = age;
}
string m_name;
int m_age;
};
class compare_person
{
public:
bool operator()(const person& p1,const person& p2) const
{
//按年龄降序排列
return p1.m_age > p2.m_age;
}
};
void test8()
{
//对于自定义数据类型,要指定排序规则
set<person,compare_person> s;
person p1("刘备", 35);
person p2("曹操", 38);
person p3("孙策", 30);
person p4("关羽", 34);
person p5("张飞", 31);
person p6("赵云", 28);
s.insert(p1);
s.insert(p2);
s.insert(p3);
s.insert(p4);
s.insert(p5);
s.insert(p6);
for (set<person,compare_person>::iterator it = s.begin(); it != s.end(); it++)
{
cout << "姓名:" << it->m_name << "\t年龄:" << it->m_age << endl;
}
}
3.8 map/multimap容器
3.8.1 map基本概念
简介:
- map中所有元素都是pair
- pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值)
- 所有元素会根据键值自动排序
本质:
- map/multimap属于关联式容器,底层结构是二叉树
优点:
- 可以根据key值快速找到value值
区别:
- map中不允许有重复key值元素
- multimap中允许有重复key值元素
3.8.1 map构造和赋值
void print_map(map<int, int>& m)
{
for (map<int, int>::iterator it = m.begin(); it != m.end(); it++)
{
cout << "key=" << (*it).first << "\tvalue=" << it->second << endl;
}
cout << endl;
}
//构造和赋值
void test1()
{
map<int, int> m;//默认构造,模板参数列表有两个参数
m.insert(pair<int, int>(1, 10));
m.insert(pair<int, int>(2, 20));
m.insert(pair<int, int>(4, 40));
m.insert(pair<int, int>(3, 30));
m.insert(pair<int, int>(5, 50));
print_map(m);//默认按key值升序
map<int, int> m2(m);//拷贝构造
print_map(m2);
map<int, int> m3 = m;//operator=
print_map(m3);
}
3.8.2 map大小和交换
与vector类似
3.8.3 map插入和删除
//插入和删除
void test3()
{
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;//第四种(不建议)
print_map(m);
//不建议使用[]插入,可以用key访问到value
cout << m[4] << endl;
m.erase(m.begin());//删除第一个元素
print_map(m);
m.erase(3);//删除key为3的元素(只能按key删)
print_map(m);
//m.erase(m.begin(), m.end());
m.clear();//清空
print_map(m);
}
3.8.4 map查找和统计
//查找和统计
void test4()
{
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 << "\tvalue=" << pos->second << endl;
}
else
{
cout << "未找到元素" << endl;
}
int num = m.count(3);
cout << "num=" << num << endl;//num只能是0/1
}
3.8.5 map容器排序
//排序
class my_compare
{
public:
bool operator()(int v1, int v2) const
{
//降序
return v1 > v2;
}
};
void test5()
{
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>(4, 40));
m.insert(pair<int, int>(5, 50));
print_map(m);//默认按key从小到大排序
map<int, int, my_compare> m2;
m2.insert(pair<int, int>(2, 40));
m2.insert(pair<int, int>(3, 20));
m2.insert(pair<int, int>(4, 40));
m2.insert(pair<int, int>(1, 10));
m2.insert(pair<int, int>(5, 90));
for (map<int, int,my_compare>::iterator it = m2.begin(); it != m2.end(); it++)
{
cout << "key=" << (*it).first << "\tvalue=" << it->second << endl;
}
cout << endl;
}
4. STL函数对象
4.1 函数对象
4.1.1 函数对象概念
概念:
- 重载函数调用操作符的类,其对象称为函数对象
- 函数对象使用重载的()时,行为类似函数调用,也叫仿函数
本质:
- 函数对象(仿函数)是一个类,不是一个函数
4.1.2 函数对象使用
特点:
- 函数对象在使用时,可以像普通函数那样调用,可以有参数,可以有返回值
- 函数对象超出普通函数的概念,函数对象可以有自己的状态
- 函数对象可以作为参数传递
class my_add
{
public:
int operator()(int v1, int v2)
{
return v1 + v2;
}
};
//函数对象在使用时,可以像普通函数那样调用,可以有参数也可以有返回值
void test1()
{
my_add myadd;
cout << myadd(10, 20) << endl;
}
class my_print
{
public:
my_print()
{
this->count = 0;
}
void operator()(string test)
{
cout << test << endl;
count++;//每调用一次该函数,count++
}
int count = 0;//记录内部状态
};
//函数对象超出普通函数的概念,函数对象可以有自己的状态
void test2()
{
my_print myprint;
myprint("fuck trae young");
myprint("fuck trae young");
myprint("fuck trae young");
cout << "fuck trae young的次数为:" << myprint.count << endl;
}
//函数对象可以作为参数传递
void do_print(my_print& mp,string test)
{
mp(test);
}
void test3()
{
my_print myprint;
do_print(myprint, "fuck trae young");
}
4.2 谓词
4.2.1 谓词概念
- 返回bool类型的仿函数称为谓词
- 如果operator()接受一个参数,那么叫做一元谓词
- 如果operator()接受两个参数,那么叫做二元谓词
4.2.2 一元谓词
class greater_than_five
{
public:
//一元谓词
bool operator()(int val)
{
return val > 5;
}
};
void test1()
{
vector<int> v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
//查找容器中有无大于5的数字
vector<int>::iterator it=find_if(v.begin(), v.end(), greater_than_five());//最后一个参数是匿名函数对象
if (it == v.end())
{
cout << "未找到" << endl;
}
else
{
cout << "找到大于5的数字为:" << *it << endl;
}
}
4.2.3 二元谓词
//使用函数对象改变排序规则
class my_compare
{
public:
bool operator()(int v1, int v2)
{
return v1 > v2;
}
};
void test2()
{
vector<int> v;
v.push_back(1);
v.push_back(4);
v.push_back(3);
v.push_back(2);
v.push_back(5);
sort(v.begin(), v.end());//默认升序
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
sort(v. begin(), v.end(), my_compare());//改为降序
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
4.3 内建函数对象
4.3.1 内建函数对象
概念:
- STL内建了一些函数对象
分类:
- 算术仿函数
- 关系仿函数
- 逻辑仿函数
用法:
- 这些仿函数所产生的对象,用法和一般函数完全相同
- 使用内建函数对象,要引入头文件
include<functional>;
4.3.2 算术仿函数
//算术仿函数
void test1()
{
negate<int> n;//取反仿函数
cout << n(10) << endl;
plus<int> p;//加法仿函数
cout << p(1, 2) << endl;
}
4.3.3 关系仿函数
//关系仿函数
void test2()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(4);
v.push_back(3);
v.push_back(5);
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
sort(v.begin(), v.end(), greater<int>());//大于仿函数(内建函数对象)
//默认 less<int>()
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
4.3.4 逻辑仿函数
//逻辑仿函数
void test3()
{
vector<bool> v;
v.push_back(true);
v.push_back(true);
v.push_back(true);
v.push_back(false);
for (vector<bool>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
//利用逻辑非,将容器v搬运到v2中,并取反
vector<bool> v2;
v2.resize(v.size());
transform(v.begin(), v.end(), v2.begin(), logical_not<bool>());//逻辑仿函数取反
for (vector<bool>::iterator it = v2.begin(); it != v2.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
5. 常用算法
5.1 常用遍历算法
- for_each //遍历容器
- transform //搬运容器到另一个容器中
//普通函数
void print1(int val)
{
cout << val << " ";
}
//仿函数
class myprint
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
void test1()
{
vector<int> v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
for_each(v.begin(), v.end(), print1);//起始迭代器、终止迭代器、普通函数
cout << endl;
for_each(v.begin(), v.end(), myprint()); //起始迭代器、终止迭代器、匿名函数对象
cout << endl;
}
//仿函数
class Transform
{
public:
int operator()(int val)
{
return val;
}
};
void test2()
{
vector<int> v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
vector<int> v_target;//目标容器
v_target.resize(v.size());//目标容器要开辟空间
transform(v.begin(), v.end(), v_target.begin(), Transform());
for_each(v_target.begin(), v_target.end(), myprint());
cout << endl;
}
5.2 常用查找算法
- find //查找元素
- find_if //按条件查找元素
- adjacent_find //查找相邻重复元素
- binary_search //二分查找法
- count //统计元素个数
- count_if //按条件统计元素个数
//find查找内置数据类型
void test1()
{
vector<int> v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
vector<int>::iterator pos = find(v.begin(), v.end(), 5);//起始迭代器、结束迭代器、值
if (pos != v.end())
{
cout << "找到元素:" << *pos << endl;
}
else
{
cout << "未找到" << endl;
}
}
//find查找自定义数据类型
class person
{
public:
person(string name, int age)
{
this->m_name = name;
this->m_age = age;
}
//重载==号,让find知道如何对比peron数据类型
/*bool operator==(const person& p)
{
if (this->m_name == p.m_name && this->m_age == p.m_age)
{
return true;
}
else
{
return false;
}
}*/
//重载==号,让count知道如何对比peron数据类型
bool operator==(const person& p)
{
if (this->m_age == p.m_age)
{
return true;
}
else
{
return false;
}
}
string m_name;
int m_age;
};
void test2()
{
vector<person> v;
person p1("aaa", 20);
person p2("bbb", 30);
person p3("ccc", 40);
person p4("ddd", 50);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
vector<person>::iterator pos = find(v.begin(), v.end(), p2);
if (pos != v.end())
{
cout << "找到元素 姓名:" << pos->m_name << "\t年龄:" << pos->m_age << endl;
}
else
{
cout << "未找到" << endl;
}
}
//find_if查找内置数据类型
class greater_than_five
{
public:
bool operator()(int val)
{
return val > 5;
}
};
void test3()
{
vector<int> v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
vector<int>::iterator pos;
pos=find_if(v.begin(), v.end(), greater_than_five());//起始迭代器、结束迭代器、仿函数(谓词)
if (pos != v.end())
{
cout << "找到大于5的数:" << *pos << endl;
}
else
{
cout << "未找到" << endl;
}
}
//find_if查找自定义数据类型
class greater_than_20
{
public:
bool operator()(person &p)
{
return p.m_age > 20;
}
};
void test4()
{
vector<person> v;
person p1("aaa", 20);
person p2("bbb", 30);
person p3("ccc", 40);
person p4("ddd", 50);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
vector<person>::iterator pos;
pos=find_if(v.begin(), v.end(), greater_than_20());
if (pos != v.end())
{
cout << "找到年龄大于20的人 姓名:" << pos->m_name << "\t年龄:" << pos->m_age << endl;
}
else
{
cout << "未找到" << endl;
}
}
//adjacent_find查找相邻重复元素
void test5()
{
vector<int> v;
v.push_back(0);
v.push_back(2);
v.push_back(0);
v.push_back(3);
v.push_back(1);
v.push_back(4);
v.push_back(3);
v.push_back(3);
vector<int>::iterator pos = adjacent_find(v.begin(), v.end());
if (pos != v.end())
{
cout << "找到相邻重复元素:" << *pos << endl;
}
else
{
cout << "未找到相邻重复元素" << endl;
}
}
//binary_search在有序序列中查找指定元素
void test6()
{
set<int> s;
s.insert(4);
s.insert(3);
s.insert(2);
s.insert(6);
s.insert(5);
s.insert(8);
bool ret = binary_search(s.begin(), s.end(), 3);//二分查找是否有3
if (ret)
{
cout << "找到了" << endl;
}
else
{
cout << "未找到" << endl;
}
}
//count统计元素个数(内置数据类型)
void test7()
{
vector<int> v;
v.push_back(2);
v.push_back(4);
v.push_back(3);
v.push_back(3);
v.push_back(3);
v.push_back(4);
v.push_back(1);
int num = count(v.begin(), v.end(), 3);
cout << "3的个数为:" << num << endl;
}
//count统计元素个数(自定义数据类型)
void test8()
{
vector<person> v;
person p1("刘备", 34);
person p2("关羽", 34);
person p3("张飞", 34);
person p4("曹操", 40);
person p5("赵云", 32);
person p("诸葛亮", 34);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
v.push_back(p5);
int num = count(v.begin(), v.end(), p);
cout << "和诸葛亮年龄一样的人的个数为:" << num << endl;
}
//count_if按条件统计元素个数(内置数据类型)
class less_than_3
{
public:
bool operator()(int val)
{
return val < 3;
}
};
void test9()
{
vector<int> v;
v.push_back(2);
v.push_back(4);
v.push_back(3);
v.push_back(3);
v.push_back(3);
v.push_back(4);
v.push_back(1);
int num = count_if(v.begin(), v.end(), less_than_3());//起始迭代器、结束迭代器、仿函数(谓词)
cout << "小于3的元素个数为:" << num << endl;
}
//count_if统计自定义数据类型(自定义数据类型)
class age_greater_than_32
{
public:
bool operator()(const person& p)
{
return p.m_age > 32;
}
};
void test10()
{
vector<person> v;
person p1("刘备", 34);
person p2("关羽", 34);
person p3("张飞", 34);
person p4("曹操", 40);
person p5("赵云", 32);
person p("诸葛亮", 34);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
v.push_back(p5);
//统计大于32的人数
int num = count_if(v.begin(), v.end(), age_greater_than_32());
cout << "年龄大于32的人数为:" << num << endl;
}
5.3 常用排序算法
- sort //对容器内元素进行排序
- random_shuffle //洗牌 指定范围内元素随机调整次序
- merge //容器元素合并,并存储到另一容器中
- reverse //反转指定范围的元素
//sort排序算法
void my_print(int val)
{
cout << val << " ";
}
void test1()
{
vector<int> v;
v.push_back(4);
v.push_back(3);
v.push_back(2);
v.push_back(1);
v.push_back(5);
sort(v.begin(), v.end());//默认升序
for_each(v.begin(), v.end(), my_print);
cout << endl;
sort(v.begin(), v.end(), greater<int>());//改为从大到小,最后一个参数是内建仿函数(谓词)
for_each(v.begin(), v.end(), my_print);
cout << endl;
}
//random_shuffle洗牌
void test2()
{
srand((unsigned int)time(NULL));
vector<int> v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
random_shuffle(v.begin(), v.end());//打乱v中元素的顺序
for_each(v.begin(), v.end(), my_print);
cout << endl;
}
//merge合并两个容器并放到另一个容器中
//v1、v2必须是有序的且顺序相同,合并之后的v也是有序的
void test3()
{
vector<int> v1;
vector<int> v2;
vector<int> v;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
v2.push_back(i + 1);
}
v.resize(v1.size() + v2.size());//给v分配空间
merge(v1.begin(), v1.end(), v2.begin(), v2.end(), v.begin());
for_each(v.begin(), v.end(), my_print);
cout << endl;
}
/reverse将容器内元素与反转
void test4()
{
vector<int> v;
v.push_back(6);
v.push_back(4);
v.push_back(2);
v.push_back(3);
v.push_back(1);
cout << "反转前:" << endl;
for_each(v.begin(), v.end(), my_print);
cout << endl;
reverse(v.begin(),v.end());
cout << "反转后:" << endl;
for_each(v.begin(), v.end(), my_print);
cout << endl;
}
5.4 常用拷贝和替换算法
- copy //容器内指定范围的元素拷贝到另一容器中
- replace //容器内指定范围的旧元素修改为新元素
- replace_if //容器内指定范围满足条件的元素替换为新元素
- swap //互换两个容器的元素
//copy将指定范围的元素复制到另一容器
void my_print(int val)
{
cout << val << " ";
}
void test1()
{
vector<int> v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
vector<int> v2;
v2.resize(v.size());//目标容器要开辟空间
copy(v.begin(), v.end(), v2.begin());
for_each(v2.begin(), v2.end(), my_print);
cout << endl;
}
//replace将指定范围内的元素改为新元素
class myprint
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
void test2()
{
vector<int> v;
v.push_back(20);
v.push_back(30);
v.push_back(10);
v.push_back(80);
v.push_back(20);
v.push_back(20);
v.push_back(50);
cout << "替换前:" << endl;
for_each(v.begin(), v.end(), myprint());//仿函数(函数对象)
cout << endl;
replace(v.begin(), v.end(), 20, 200);//将v中的20替换为200
cout << "替换后:" << endl;
for_each(v.begin(), v.end(), myprint());//仿函数(函数对象)
cout << endl;
}
//replace_if将指定范围内满足条件的元素改为新元素
class greater_than_30
{
public:
bool operator()(int val)
{
return val > 30;
}
};
void test3()
{
vector<int> v;
v.push_back(20);
v.push_back(30);
v.push_back(10);
v.push_back(80);
v.push_back(20);
v.push_back(20);
v.push_back(50);
//将大于30的数替换为300
cout << "替换前:" << endl;
for_each(v.begin(), v.end(), myprint());//仿函数(函数对象)
cout << endl;
replace_if(v.begin(), v.end(), greater_than_30(), 300);//谓词(函数对象)
cout << "替换后:" << endl;
for_each(v.begin(), v.end(), myprint());//仿函数(函数对象)
cout << endl;
}
//swap互换两个同类型的容器
void test4()
{
vector<int> v1;
vector<int> v2;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
v2.push_back(i + 2);
}
cout << "交换前:" << endl;
for_each(v1.begin(), v1.end(), myprint());
cout << endl;
for_each(v2.begin(), v2.end(), myprint());
cout << endl;
swap(v1, v2);
cout << "交换后:" << endl;
for_each(v1.begin(), v1.end(), myprint());
cout << endl;
for_each(v2.begin(), v2.end(), myprint());
cout << endl;
}
5.5 常用算术生成算法
- accumulate //计算容器元素累计总和
accumulate(v.begin(), v.end(), 0);//0是起始累加值
- fill //向容器中添加元素
fill(v.begin(), v.end(), 100);//用100填充
5.6 常用集合算法
- set_intersection //求两个容器的交集
- 求交集的两个容器必须是有序序列
- 目标容器开辟空间取两个容器最小值
- 返回值是交集中最后一个元素的迭代器
- set_union //求两个容器的并集
- set_difference //求两个容器的差集(独有的元素)
//set_intersection求交集
//两个集合必须是有序序列
class myprint
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
void test1()
{
vector<int> v1;
vector<int> v2;
vector<int> v;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);//0~9
v2.push_back(i + 2);//2~11
}
//目标容器要开辟空间,最特殊的就是一个大容器包含一个小容器
v.resize(min(v1.size(), v2.size()));
vector<int>::iterator pos_end;
//返回目标容器最后一个元素的迭代器位置
pos_end = set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), v.begin());
for_each(v.begin(), pos_end, myprint());
cout << endl;
}
//set_union求并集
void test2()
{
vector<int> v1;
vector<int> v2;
vector<int> v;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);//0~9
v2.push_back(i + 2);//2~11
}
//目标容器要开辟空间,最特殊的就是没有交集
v.resize(v1.size()+v2.size());
vector<int>::iterator pos_end;
//返回目标容器最后一个元素的迭代器位置
pos_end = set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), v.begin());
for_each(v.begin(), pos_end, myprint());
cout << endl;
}
//set_difference求差集(独有的元素)
void test3()
{
vector<int> v1;
vector<int> v2;
vector<int> v;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);//0~9
v2.push_back(i + 2);//2~11
}
//目标容器要开辟空间,最特殊的是两个容器没有交集
v.resize(max(v1.size(), v2.size()));
vector<int>::iterator pos_end;
//返回目标容器最后一个元素的迭代器位置
cout << "v1和v2的差集:" << endl;
pos_end = set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), v.begin());
for_each(v.begin(), pos_end, myprint());
cout << endl;
cout << "v2和v1的差集:" << endl;
pos_end = set_difference(v2.begin(), v2.end(), v1.begin(), v1.end(), v.begin());
for_each(v.begin(), pos_end, myprint());
cout << endl;
}