【C++】- STL讲解

// test.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include 

//第一章 迭代器
//要访问顺序容器和关联容器中的元素,需要通过“迭代器(iterator)”进行。迭代器是一个变量,相当于容器和操纵容器的算法之间的中介。
//迭代器可以指向容器中的某个元素,通过迭代器就可以读写它指向的元素。从这一点上看,迭代器和指针类似。

/*
迭代器按照定义方式分成以下四种。

正向迭代器,定义方法如下:
容器类名::iterator 迭代器名;

常量正向迭代器,定义方法如下:
容器类名::const_iterator 迭代器名;

反向迭代器,定义方法如下:
容器类名::reverse_iterator 迭代器名;

常量反向迭代器,定义方法如下:
容器类名::const_reverse_iterator 迭代器名;
*/

/*
迭代器用法示例
通过迭代器可以读取它指向的元素,*迭代器名就表示迭代器指向的元素。通过非常量迭代器还能修改其指向的元素。

迭代器都可以进行++操作。反向迭代器和正向迭代器的区别在于:
对正向迭代器进行++操作时,迭代器会指向容器中的后一个元素;
而对反向迭代器进行++操作时,迭代器会指向容器中的前一个元素。

下面的程序演示了如何通过迭代器遍历一个 vector 容器中的所有元素。
*/

#include 
#include 
using namespace std;
void iterator_use1()
{
	cout << "iterator_use1 start";
	vector v;  //v是存放int类型变量的可变长数组,开始时没有元素
	for (int n = 0; n<5; ++n)
		v.push_back(n);  //push_back成员函数在vector容器尾部添加一个元素
	vector::iterator i;  //定义正向迭代器
	//begin 成员函数返回指向容器中第一个元素的迭代器。++i 使得 i 指向容器中的下一个元素。end 成员函数返回的不是指向最后一个元素的迭代器,而是指向最后一个元素后面的位置的迭代器,因此循环的终止条件是i != v.end()。
	for (i = v.begin(); i != v.end(); ++i) {  //用迭代器遍历容器
		cout << *i << " ";  //*i 就是迭代器i指向的元素
		*i *= 2;  //每个元素变为原来的2倍
	}
	cout << endl;
	//用反向迭代器遍历容器
	for (vector::reverse_iterator j = v.rbegin(); j != v.rend(); ++j)
		cout << *j << " ";
	cout << "iterator_use1 end";
}

/*
如果迭代器指向了容器中最后一个元素的后面或第一个元素的前面,再通过该迭代器访问元素,就有可能导致程序崩溃,这和访问 NULL 或未初始化的指针指向的地方类似。

写++i、++j相比于写i++、j++,程序的执行速度更快。回顾++被重载成前置和后置运算符的例子如下:
CDemo CDemo::operator++ ()
{ //前置++
	++n;
	return *this;
}
CDemo CDemo::operator ++(int k)
{ //后置++
	CDemo tmp(*this); //记录修改前的对象
	n++;
	return tmp; //返回修改前的对象
}
后置++要多生成一个局部对象 tmp,因此执行速度比前置的慢。同理,迭代器是一个对象,STL 在重载迭代器的++运算符时,后置形式也比前置形式慢。在次数很多的循环中,++i和i++可能就会造成运行时间上可观的差别了。因此,本教程在前面特别提到,对循环控制变量i,要养成写++i、不写i++的习惯。

注意,容器适配器 stack、queue 和 priority_queue 没有迭代器。容器适配器有一些成员函数,可以用来对元素进行访问。
迭代器的功能分类
不同容器的迭代器,其功能强弱有所不同。容器的迭代器的功能强弱,决定了该容器是否支持 STL 中的某种算法。例如,排序算法需要通过随机访问迭代器来访问容器中的元素,因此有的容器就不支持排序算法。

常用的迭代器按功能强弱分为输入、输出、正向、双向、随机访问五种,这里只介绍常用的三种。

1.正向迭代器。假设 p 是一个正向迭代器,则 p 支持以下操作:++p,p++,*p。此外,两个正向迭代器可以互相赋值,还可以用 == 和 != 运算符进行比较。

2.双向迭代器。双向迭代器具有正向迭代器的全部功能。除此之外,若 p 是一个双向迭代器,则–p和p–都是有定义的。–p使得 p 朝和++p相反的方向移动。

3.随机访问迭代器。随机访问迭代器具有双向迭代器的全部功能。若 p 是一个随机访问迭代器,i 是一个整型变量或常量,则 p 还支持以下操作:
p += i:使得 p 往后移动 i 个元素。
p -= i:使得 p 往前移动 i 个元素。
p + i:返回 p 后面第 i 个元素的迭代器。
p - i:返回 p 前面第 i 个元素的迭代器。
p[i]:返回 p 后面第 i 个元素的引用。

此外,两个随机访问迭代器 p1、p2 还可以用 <、>、<=、>= 运算符进行比较。p1 v(100); //v被初始化成有100个元素
	for (int i = 0; i < v.size(); ++i) //size返回元素个数
		cout << v[i]; //像普通数组一样使用vector容器
	vector::iterator i;
	for (i = v.begin(); i != v.end(); ++i) //用 != 比较两个迭代器
		cout << *i;
	for (i = v.begin(); i < v.end(); ++i) //用 < 比较两个迭代器
		cout << *i;
	i = v.begin();
	while (i < v.end()) { //间隔一个输出
		cout << *i;
		i += 2; // 随机访问迭代器支持 "+= 整数"  的操作
	}
	cout << "iterator_use2 end";
}

/*
list 容器的迭代器是双向迭代器。假设 v 和 i 的定义如下:
list v;
list::const_iterator i;
则以下代码是合法的:
for (i = v.begin(); i != v.end(); ++i)
cout << *i;
以下代码则不合法:
for (i = v.begin(); i
void iterator_use3()
{
	cout << "iterator_use3 start";
	int a[5] = { 1, 2, 3, 4, 5 };
	list  lst(a, a + 5);
	list ::iterator p = lst.begin();
	advance(p, 2);  //p向后移动两个元素,指向3
	cout << "1)" << *p << endl;  //输出 1)3
	advance(p, -1);  //p向前移动一个元素,指向2
	cout << "2)" << *p << endl;  //输出 2)2
	list::iterator q = lst.end();
	q--;  //q 指向 5
	cout << "3)" << distance(p, q) << endl;  //输出 3)3
	iter_swap(p, q); //交换 2 和 5
	cout << "4)";
	for (p = lst.begin(); p != lst.end(); ++p)
		cout << *p << " ";
	cout << "iterator_use3 end";
}

//第二章 
//pair模板类用来将两个对象表示成一个对象。
//用途:1)想要函数同时返回两个参数; 2)想要用一个容器存储成对值的元素

/*
我们知道,关联式容器存储的是“键值对”形式的数据,比如:
<"一", "1">
<"二", "2">
<"三", "3">

如上所示,每行都表示一个键值对,其中第一个元素作为键(key),第二个元素作为值(value)。
*/

/*
考虑到“键值对”并不是普通类型数据,C++ STL 标准库提供了 pair 类模板,其专门用来将 2 个普通元素 first 和 second(可以是 C++ 基本数据类型、结构体、类自定的类型)创建成一个新元素。通过其构成的元素格式不难看出,使用 pair 类模板来创建“键值对”形式的元素,再合适不过。

注意,pair 类模板定义在头文件中,所以在使用该类模板之前,需引入此头文件。另外值得一提的是,在 C++ 11 标准之前,pair 类模板中提供了以下 3 种构造函数:
#1) 默认构造函数,即创建空的 pair 对象
pair();
#2) 直接使用 2 个元素初始化成 pair 对象
pair (const first_type& a, const second_type& b);
#3) 拷贝(复制)构造函数,即借助另一个 pair 对象,创建新的 pair 对象
template pair (const pair& pr);

在 C++ 11 标准中,在引入右值引用的基础上,pair 类模板中又增添了如下 2 个构造函数:
#4) 移动构造函数
template pair (pair&& pr);
#5) 使用右值引用参数,创建 pair 对象
template pair (U&& a, V&& b);

除此之外,C++ 11 标准中 pair 类模板还新增加了如下一种构造函数:
pair (piecewise_construct_t pwc, tuple first_args, tuple second_args);
,该构造 pair 类模板的方式很少用到,因此本节不再对其进行详细介绍,感兴趣可自行查阅资料。
*/

#include 
void pair_use1()
{
	cout << "pair_use1 start";
	// 调用构造函数 1,也就是默认构造函数
	pair  pair1;
	// 调用第 2 种构造函数
	pair  pair2("一", "1");
	// 调用拷贝构造函数
	pair  pair3(pair2);
	//调用移动构造函数
	pair  pair4(make_pair("二", "2"));
	// 调用第 5 种构造函数
	pair  pair5(string("三"), string("3"));

	cout << "pair1: " << pair1.first << " " << pair1.second << endl;
	cout << "pair2: " << pair2.first << " " << pair2.second << endl;
	cout << "pair3: " << pair3.first << " " << pair3.second << endl;
	cout << "pair4: " << pair4.first << " " << pair4.second << endl;
	cout << "pair5: " << pair5.first << " " << pair5.second << endl;
	cout << "pair_use1 end";
}

/*
上面程序在创建 pair4 对象时,调用了 make_pair() 函数,它也是  头文件提供的,其功能是生成一个 pair 对象。因此,当我们将 make_pair() 函数的返回值(是一个临时对象)作为参数传递给 pair() 构造函数时,其调用的是移动构造函数,而不是拷贝构造函数。

在上面程序的基础上,C++ 11 还允许我们手动为 pair1 对象赋值,比如:
pair1.first = "一";
pair1.second = "1";
cout << "new pair1: " << pair1.first << " " << pair1.second << endl;
执行结果为:
new pair1: 一 1

同时,上面程序中 pair4 对象的创建过程,还可以写入如下形式,它们是完全等价的:
pair  pair4 = make_pair("二", "2");
cout << "pair4: " << pair4.first << " " << pair4.second << endl;

头文件中除了提供创建 pair 对象的方法之外,还为 pair 对象重载了 <、<=、>、>=、==、!= 这 6 的运算符,其运算规则是:对于进行比较的 2 个 pair 对象,先比较 pair.first 元素的大小,如果相等则继续比较 pair.second 元素的大小。
注意,对于进行比较的 2 个 pair 对象,其对应的键和值的类型比较相同,否则将没有可比性,同时编译器提示没有相匹配的运算符,即找不到合适的重载运算符。

举个例子:
*/

void pair_use2()
{
	cout << "pair_use2 start";
	pair  pair1("一", 10);
	pair  pair2("二", 10);
	pair  pair3("二", 20);
	//pair1和pair2的key不同,value相同
	if (pair1 != pair2) {
		cout << "pair != pair2" << endl;
	}
	//pair2和pair3的key相同,value不同
	if (pair2 != pair3) {
		cout << "pair2 != pair3" << endl;
	}
	cout << "pair_use2 end";
}

/*
最后需要指出的是,pair类模板还提供有一个 swap() 成员函数,能够互换 2 个 pair 对象的键值对,其操作成功的前提是这 2 个 pair 对象的键和值的类型要相同。例如:
*/
void pair_use3()
{
	cout << "pair_use3 start";
	pair  pair1("pair", 10);
	pair  pair2("pair2", 20);
	//交换 pair1 和 pair2 的键值对
	pair1.swap(pair2);
	cout << "pair1: " << pair1.first << " " << pair1.second << endl;
	cout << "pair2: " << pair2.first << " " << pair2.second << endl;
	cout << "pair_use3 end";
}

//第三章 
/*
智能指针:包含重载运算符的类,其行为像常规指针,但智能指针能够及时、妥善地销毁动态分配的数据,并实现了明确的对象生命周期,因此更有价值。
常规指针存在的问题
避免资源泄露,传统手动销毁资源new和delete,很容易忘记delete导致内存泄露
为了更加安全的的使用动态内存,引入智能指针的概念。智能指针的行为类似常规指针,最大的区别是它能够自动释放所指向的对象,利用智能指针管理动态内存可以有效降低内存泄漏的可能。
*/

/*
//几个常见的智能指针:
1. auto_ptr
auto_ptr类似于指针变量:提供了*解引用和->箭头取成员函数或对象操作。

但是,auto_ptr提供严格的所有者权限审察,一个auto_ptr对象管理它指向的资源,而这个资源不应该被其他对象拥有,不幸的是C++语言没有在语言级别上限制,需要程序员自己控制。导致这个情况的是拷贝构造函数和赋值操作符的处理。

std::auto_ptr ptr1(new ClassA);
std::auto_ptr ptr2(ptr1);
//now ptr2 has the ownership, when ptr2 delete and the object destroyed

std::auto_ptr ptr1(new ClassA);
std::auto_ptr ptr2;
ptr2 = ptr1;  //copy assignment
//效果同上


函数可以利用auto_ptr来传递所有权:

void sink(auto_ptr);  //sink get the ownership

	//函数返回值拥有所有权
	auto_ptr f()
	{
	auto_ptr ptr(new ClassA);
	return ptr; //将所有权移交给函数调用者
}

当你不打算转移auto_ptr所有权的时候千万不要用它作为函数参数或者返回值,因为这很危险:
template 
void bad_print(auto_ptr p)
{
	if(p.get() == NULL)
	{
		cout << "NULL" << endl;
	} 
	else
	{
		cout << *p << endl;
	}
}

客户端这样使用:
auto_ptr p(new int);
*p = 10;
bad_print(p);  //delete the memory that p refer
*p = 20;  //runtime error


避免传递auto_ptr的引用到函数:
const 意味着你不能改变auto_ptr的所有权而非指针所指向的对象的值:
auto_ptr void f()
{
	const auto_ptr p(new int); //no ownership transfer
	auto_ptr q(new int); //ownership transfer possible

	*p = 42; //ok
	bad_print(p);  //complie-time error
	*p = *q; //ok
	p = q; //error
	return p;  //error
}

当指针变量作为class的成员变量:不安全做法,可能导致资源泄露

class B {
private:
	A *pA1;
	A *pA2;
public:
	//当第二个new抛异常时候可能会导致资源泄露
	B(A a1, A a2)
	: pA1(new A(a1)), pA2(new A(a2))
	{ }
	//同上
	B(const B& x)
	: pA1(new A(*x.pA1)), pA2(new A(*x.pA2))
	//拷贝运算符
	const B& operator=(const B& x)
	{
	*pA1 = *x.pA1;
	*pA2 = *x.pA2;
	return *this;
	}

	//析构函数
	~B()
	{
	delete pA1;
	delete pA2;
	}
};

为了避免上述资源泄露,使用auto_ptr改进:
class B {
private:
const auto_ptr pA1;
const auto_ptr pA2;
public:
//当第二个new抛异常时候可能会导致资源泄露
B(A a1, A a2)
: pA1(new A(a1)), pA2(new A(a2))
{ }
//同上
B(const B& x)
: pA1(new A(*x.pA1)), pA2(new A(*x.pA2))
//拷贝运算符
const B& operator=(const B& x)
{
*pA1 = *x.pA1;
*pA2 = *x.pA2;
return *this;
}

//这里就不需要析构函数
};


注意:auto_ptr使用指南

不能共享所有权

不能应用于array,auto_ptr call delete not delete[]

不是通用的智能指针

针对容器,无用武之地:因为coping不是常规的复制,而是transfer所有权


一个小例子:auto_ptr转移所有权,注意const auto_ptr& p const保证了函数不会转移所有权

#include 
#include    //auto_ptr头文件

using namespace std;

//为auto_ptr定义输出操作符<<
template 
ostream& operator<<(ostream& os, const auto_ptr& p)
{
if(p.get() == NULL)
{
os << "NULL";
} else
{
os << *p;
}
return os;
}

int main()
{
auto_ptr p(new int(42));
auto_ptr q;

cout << "after initialization : " << endl;
cout << "p = " << p << endl;  //p = 42
cout << "q = " << q << endl;  //q = NULL

q = p;

cout << "after assigning auto_ptr : " << endl;
cout << "p = " << p << endl;  //p = NULL
cout << "q = " << q << endl;  //q = 42

*q = *q + 58;
p = q;

cout << "after change *q and reassign : " << endl;
cout << "p = " << p << endl;  //p = 100
cout << "q = " << q << endl;  //q = NULL
return 0;
}


auto_ptr p = new int(9);  //error, 因为auto_ptr对基础类型的构造函数是explicit不能进行隐式转换

下面的小例子展示了auto_ptr的const用法:

#include 
#include    //auto_ptr头文件

using namespace std;

//为auto_ptr定义输出操作符<<
template 
ostream& operator<<(ostream& os, const auto_ptr& p)
{
if(p.get() == NULL)
{
os << "NULL";
} else
{
os << *p;
}
return os;
}

int main()
{
const auto_ptr p(new int(42));
const auto_ptr q(new int(0));
const auto_ptr r;

cout << "after initialization : " << endl;
cout << "p = " << p << endl;  //p = 42
cout << "q = " << q << endl;  //q = 0
cout << "r = " << r << endl;  // r = NULL

*q = *p;
//*r = *p; //error at compile
*p = -77;

cout << "after assigning values auto_ptr point to: " << endl;
cout << "p = " << p << endl;  //p = -77
cout << "q = " << q << endl;  //q = 42
cout << "r = " << r << endl;  // r = NULL

//q = p; //compile error
//r = p; //compile error
return 0;
}
*/


/*
shared_ptr:允许多个指针指向同一个对象(引用计数器机制)
shared_ptr 是C++11提供的一种智能指针类,它可以自动删除相关指针,从而彻底消除内存泄漏和悬空指针的问题。
它遵循共享所有权的概念,即不同的 shared_ptr 对象可以与相同的指针相关联,内部使用引用计数机制来实现。

1.引用计数机制:
每个 shared_ptr 对象在内部指向两个内存位置:指向对象的指针和用于控制引用计数数据的指针。
1、当新的 shared_ptr 对象与指针关联时,在其构造函数中,将与此指针关联的引用计数增加1。
2、当任何 shared_ptr 对象超出作用域时,则在其析构函数中,它将关联指针的引用计数减1。
3、如果引用计数变为0,则无shared_ptr 对象与此内存关联,它使用delete函数删除该内存。

2.shared_ptr支持的操作:

shared_ptr p      //空智能指针
p                    //将p用作一个条件判断,若p指向一个对象则为ture
*p                   //解引用p,获得它指向的对象
p->member            //等价于(*p).member
p.get()              //返回p中保存的指针,小心使用
swap(p,q)            //交换p和q中的指针
p.swap(q)            //交换p和q中的指针
make_shared(args) //返回一个动态分配类型为T的对象,使用args初始化此对象
shared_ptrp(q)    //p 是shared_ptr q的拷贝,此操作会递增q中的计数器,q中的指针必须能转换为T*
shared_ptrp(u)    //p从unique_ptr u那里接管了对象的所有权,将u置空
shared_ptrp(q,d)  //p接管了内置指针q所指向的对象的所有权,p将使用可调用对象d来代替delete
p=q                  //p和q必须都是shared_ptr,所保存的指针必须能相互转换,此操作会递减p的引用计数,递增q的引用计数
p.use_count()        //返回与p共享对象的智能指针数量,可能很慢,主要用于调试
p.unique()           //若p.use_count()为1,返回ture,否则返回false
p.reset()            //分离关联的原始指针,p指向的对象引用计数减少1
p.reset(q)           //令p指向新指针q
p.reset(q,d)         //令p指向q,并调用d来代替detlete
p = nullptr          //重置指针为空指针

3.shared_ptr的定义
智能指针的本质是一个模板,和普通指针定义类似,定义指针时需要声明指针可以指向值的类型。定义普通指针时,类型写在开头,定义智能指针则把类型写在尖括号里。

//普通指针定义
int *pi;

//智能指针定义(默认初始化为空指针nullptr)
std::shared_ptr p1;               //允许指向string类型的空智能指针
std::shared_ptr p2;                  //允许指向int类型的空智能指针
std::shared_ptr> p3;       //允许只想vector类型的空智能指针

4.shared_ptr的初始化
使用make_shared()函数初始化智能指针(常用)
通过传入的参数与构造类型的构造函数进行匹配并初始化,也可以不传入任何参数,默认使用值初始化的方式初始化对象。make_shared()函数make_share后面要加一个尖括号指出指针要指向的类型。

//eg1:
std::shared_ptrp1 = std::make_shared();
//eg2:
std::shared_ptrp1 = std::make_shared(10);
//eg2:
std::shared_ptrp2 = std::make_shared("I am a shared_ptr")

5.shared_ptr的拷贝与复制
shared_ptr在内部实现上有一个引用计数器,当进行拷贝和赋值时,每个shared_ptr都会记录有多少个其他shared_ptr指向相同的对象。只要拷贝一个shared_ptr,引用计数器的值就会上升,一旦引用计数器的值为0,它就会自动调用shared_ptr指向类型的析构函数释放相关联的动态内存。

auto p = make_shared42;
auto q(p);//拷贝构造
r=q;//给r赋值,让它指向另一个地址
//递增q指向的对象的引用计数
//递减r原来指向的对象的引用计数
//若r原来指向的对象已经没有引用者,会自动释放

使用动态内存的三种情况:
1.程序不知道自己需要多少对象
2.程序不知道所需对象的准确类型
3.程序需要在多个对象间共享数据

6.shared_ptr与new一起使用
(1)new运算符分配空间

//没有赋初始值,采用默认初始化
int* p = new int;
string* p = new string;
//值初始化,p1指向一块int类型的动态内存,内置类型int会被赋值0
int *p1 = new int();
string *p1 = new string();
//直接初始化,p2指向一块int类型的动态内存,其初始化值为1024
int *p2 = new int(1024);
string *p2 = new string("hk");
vector vp = new vector{0,1,2,3,4,5};

//内存耗尽时,new表达式就会失败,抛出异常
int *p=new int;//如果分配失败,new抛出std::bad_alloc异常
int *p=new (nothrow)int;//如果分配失败,new返回一个空指针

(2)delete释放动态内存
为了防止内存耗尽,在动态内存使用完之后,必须将其归还给系统,使用delete归还。我们传递给delete的指针必须指向动态内存,或者是一个空指针。
释放一块并非new分配的内存或者将相同的指针释放多次,其行为是未定义的。
即使delete后面跟的是指向静态分配的对象或者已经释放的空间,编译还是能够通过,实际上是错误的。动态对象的生存周期直到被释放时为止,所以调用这必须记得释放内存。
在delete之后,指针就变成了空悬指针,即指向一块曾经保存数据对象但现在已经无效的内存的地址有一种方法可以避免悬空指针的问题:
在指针即将要离开其作用于之前释放掉它所关联的内存,如果我们需要保留指针可以在delete之后将nullptr赋予指针,这样就清楚的指出指针不指向任何对象。

(3)shared_ptr和new
如果我们不初始化一个智能指针,它就会被初始化成一个空指针,接受指针参数的职能指针是explicit的,因此我们不能将一个内置指针隐式转换为一个智能指针,必须直接初始化形式来初始化一个智能指针

std::shared_ptr p1(new int());//正确
std::shared_ptr p2 = new int();//错误
注:shared_ptr构造函数是explicit类型的,所以不能隐式调用它的构造函数,即像std::shared_ptr p1 = new int();
我们不能将内置指针赋值给shared_ptr进行初始化,而必须采用直接初始化的方法,同样的,一个返回shared_ptr的函数的返回值也不允许返回内置指针,
因为返回内置指针则是要求编译器将该内置指针隐式转换为智能指针,这是不允许的。

(4)不要混合使用普通指针和智能指针
如果混合使用的话,智能指针自动释放之后,普通指针有时就会变成悬空指针,当将一个shared_ptr绑定到一个普通指针时,我们就将内存的管理责任交给了这个shared_ptr。
一旦这样做了,我们就不应该再使用内置指针来访问shared_ptr所指向的内存了。
也不要使用get初始化另一个智能指针或为智能指针赋值。

(5)存在的陷阱
(1)不使用相同的内置指针初始化(或reset)多个智能指针
(2)不delete get()返回的指针
(3)不使用get()初始化或resel另一个指针
(4)如果使用get返回的指针,当最后一个对应的智能指针销毁后,你的指针就变为无效了
(5)如果你使用智能指针管理的资源不是new分配的内存,记住传递给它一个删除器
*/


/*
2.unique_ptr: "独占“所指对象(引用计数器最大值为1的shared_ptr)
unique_ptr 是从 C++ 11 开始,定义在  中的智能指针(smart pointer)。它持有对对象的独有权,即两个 unique_ptr 不能指向一个对象,不能进行复制操作只能进行移动操作。
unique_ptr 之所以叫这个名字,是因为它只能指向一个对象,即当它指向其他对象时,之前所指向的对象会被摧毁。其次,当 unique_ptr 超出作用域时,指向的对象也会被自动摧毁,帮助程序员实现了自动释放的功能。
unique_ptr 也可能还未指向对象,这时的状态被称为 empty。

unique_ptr 独享被管理对象指针所有权的智能指针unique_ptr对象包装一个原始指针,并负责其生命周期。
当该对象被销毁时,会在其析构函数中删除关联的原始指针。unique_ptr也是智能指针的一种,它和shared_ptr不同的是unique_ptr“拥有”它所指向的对象,
可以理解为unique_ptr是一个引用计数器最大值为1的shared_ptr,所以unique_ptr不能被赋值与拷贝,且其必须使用new的方式对其指向的对象 直接初始化。

unique_ptr支持的操作:
unique_ptr u1;             //空unique_ptr,可以指向类型为T的对象,u1会使用delete来释放它的指针
unique_ptr u2;           //空unique_ptr,可以指向类型为T的对象,u2会使用一个类型为D的可调用对象来释放它的指针
unique_ptr u(d);         //空unique_ptr,指向类型为T的对象,用类型为D的对象d代替delete
u = nullptr;                  //释放u指向的对象,将u置空
u.release();                  //u放弃对指针的控制权,返回指针,并将u置空
u.reset();                    //释放u指向的对象
u.reset(q);                   //如果提供了内置指针q,令u指向q,否则将u置为空
u.reset(nullptr);             //将u置为空

unique_ptr独享所有权
unique_ptr对象始终是关联的原始指针的唯一所有者。我们无法复制unique_ptr对象,它只能移动。
由于每个unique_ptr对象都是原始指针的唯一所有者,因此在其析构函数中它直接删除关联的指针,不需要任何参考计数。

简单示例
std::unique_ptrp1(new int(5));
std::unique_ptrp2=p1;// 编译会出错
std::unique_ptrp3=std::move(p1);// 转移所有权, 现在那块内存归p3所有, p1成为无效的针.
p3.reset();//释放内存.
p1.reset();//无效

unique_ptr 在  中的定义如下:
// non-specialized
template > class unique_ptr;
// array specialization
template  class unique_ptr;
其中 T 指其管理的对象类型,D 指该对象销毁时所调用的释放方法,可以使用自定义的删除器,他也有一个默认的实现,即 detele 操作。

创建unique_ptr对象
//创建一个空的unique_ptr对象
std::unique_ptr ptr1;
//使用原始指针创建unique_ptr对象(非空)
std::unique_ptr taskPtr(new Task(22));
std::unique_ptr taskPtr(new std::unique_ptr::element_type(23));
//不能采用赋值的方式创建unique_ptr对象
std::unique_ptr taskPtr2 = new Task();//错误
//使用std::make_unique创建  C++14新函数
std::unique_ptr taskPtr = std::make_unique(34);

//检查unique_ptr对象是否为空
//方法1
if(!ptr)
std::cout<<"ptr1 is empty"< taskPtr3 = taskPtr2;
taskPtr = taskPtr2;

(1)使用move转移所有权
//通过原始指针创建taskPtr2
std::unique_ptr taskPtr2(new Task(55));
//把taskPtr2中关联指针的所有权转移给taskPtr4
std::unique_ptrtaskPtr4 = std::move(taskPtr2);
//现在taskPtr2关联的指针为空
if(taskPtr2 == nullptr)
std::cout<<"taskPtr2 is  empty"<mId << std::endl;

(2)使用release或reset转移所有权

//将所有权从p1转移到p2,利用release
unique_ptr p2(p1.release());//release将p1置为空
//将所有权从p3转移到p2,利用reset
unique_ptr p3(new string("Trex"));
p2.reset(p3.release());//reset释放p2原来指向的内存

注:
成员返回unique_ptr当前保存的指针并将其置为空。因此,p2被初始化为p1原来保存的指针,而p1被置为空。
reset成员接受一个可选的指针参数,令unique_ptr重新指向给定的指针。
调用release会切断unique_ptr和它原来管理的的对象间的联系。release返回的指针通常被用来初始化另一个智能指针或给另一个智能指针赋值。

释放关联的原始指针
在 unique_ptr 对象上调用 release()将释放其关联的原始指针的所有权,并返回原始指针。这里是释放所有权,并没有delete原始指针,reset()会delete原始指针。

std::unique_ptr taskPtr5(new Task(55));
// 不为空
if(taskPtr5 != nullptr)
std::cout<<"taskPtr5 is not empty"<
void unique_ptr_use1()
{
	cout << "unique_ptr_use1 start";
	std::default_delete d;
	std::unique_ptr u1;//null
	std::unique_ptr u2(nullptr);//null
	std::unique_ptr u3(new int);//not null
	std::unique_ptr u4(new int, d);//not null
	std::unique_ptr u5(new int, std::default_delete());//null
	std::unique_ptr u6(std::move(u5));//null
	std::unique_ptr u7(std::move(u6));//not null
	std::unique_ptr u8(std::auto_ptr(new int));//not null

	std::cout << "u1: " << (u1 ? "not null" : "null") << '\n';
	std::cout << "u2: " << (u2 ? "not null" : "null") << '\n';
	std::cout << "u3: " << (u3 ? "not null" : "null") << '\n';
	std::cout << "u4: " << (u4 ? "not null" : "null") << '\n';
	std::cout << "u5: " << (u5 ? "not null" : "null") << '\n';
	std::cout << "u6: " << (u6 ? "not null" : "null") << '\n';
	std::cout << "u7: " << (u7 ? "not null" : "null") << '\n';

	cout << "unique_ptr_use1 end";
}

struct Task {
	int mId;
	Task(int id) :mId(id) {
		std::cout << "Task::Constructor" << std::endl;
	}
	~Task() {
		std::cout << "Task::Destructor" << std::endl;
	}
};

void unique_ptr_use2()
{
	cout << "unique_ptr_use2 start";
	// 空对象 unique_ptr
	std::unique_ptr ptr1;

	// 检查 ptr1 是否为空
	if (!ptr1)
		std::cout << "ptr1 is empty" << std::endl;

	// 检查 ptr1 是否为空
	if (ptr1 == nullptr)
		std::cout << "ptr1 is empty" << std::endl;

	// 不能通过赋值初始化unique_ptr
	// std::unique_ptr taskPtr2 = new Task(); // Compile Error

	// 通过原始指针创建 unique_ptr
	std::unique_ptr taskPtr(new Task(23));

	// 检查 taskPtr 是否为空
	if (taskPtr != nullptr)
		std::cout << "taskPtr is  not empty" << std::endl;

	// 访问 unique_ptr关联指针的成员
	std::cout << taskPtr->mId << std::endl;

	std::cout << "Reset the taskPtr" << std::endl;
	// 重置 unique_ptr 为空,将删除关联的原始指针
	taskPtr.reset();

	// 检查是否为空 / 检查有没有关联的原始指针
	if (taskPtr == nullptr)
		std::cout << "taskPtr is  empty" << std::endl;

	// 通过原始指针创建 unique_ptr
	std::unique_ptr taskPtr2(new Task(55));

	if (taskPtr2 != nullptr)
		std::cout << "taskPtr2 is  not empty" << std::endl;

	// unique_ptr 对象不能复制
	//taskPtr = taskPtr2; //compile error
	//std::unique_ptr taskPtr3 = taskPtr2;

	{
		// 转移所有权(把unique_ptr中的指针转移到另一个unique_ptr中)
		std::unique_ptr taskPtr4 = std::move(taskPtr2);
		// 转移后为空
		if (taskPtr2 == nullptr)
			std::cout << "taskPtr2 is  empty" << std::endl;
		// 转进来后非空
		if (taskPtr4 != nullptr)
			std::cout << "taskPtr4 is not empty" << std::endl;

		std::cout << taskPtr4->mId << std::endl;

		//taskPtr4 超出下面这个括号的作用于将delete其关联的指针
	}

	std::unique_ptr taskPtr5(new Task(66));

	if (taskPtr5 != nullptr)
		std::cout << "taskPtr5 is not empty" << std::endl;

	// 释放所有权
	Task * ptr = taskPtr5.release();

	if (taskPtr5 == nullptr)
		std::cout << "taskPtr5 is empty" << std::endl;

	std::cout << ptr->mId << std::endl;

	delete ptr;

	cout << "unique_ptr_use2 end";
}
/*
3.weak_ptr: 弱引用,指向shared_ptr所管理的对象
weak_ptr是一种不控制所指向对象生存期的弱引用智能指针,它指向一个由shared_ptr管理的对象,将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。
一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放,即使有weak_ptr指向对象,对象还是会被释放。
可以将它看成是一个不会增加引用计数器的shared_ptr,同时也不会负责指向对象的内存管理。

这个智能指针用的不太多,因为它本身并没有太多实际的用途,而是主要作为shared_ptr的一个辅助类存在
比如有多少指向相同的 shared_ptr 指针、shared_ptr 指针指向的堆内存是否已经被释放等等。

weak_ptr支持的操作
weak_ptr w;                   //空weak_ptr可以指向类型为T的对象
weak_ptr w(sp);               //与shared_ptr指向相同对象的weak_ptr。T必须能转换为sp指向的类型
w = p;                           //p可以是一个shared_ptr或者一个weak_ptr。赋值后w与p共享对象
w.reset()                        //将w置空
w.use_count()                    //与w共享对象的shared_ptr的数量
w.expired()                      //若w.use_count()为0返回ture,否则返回false
w.lock()                         //如果expired为ture,返回一个空shared_ptr,否则返回一个指向w的对象的shared_ptr
由于对象可能不存在,我们不能使用weak_ptr直接访问对象,而必须调用lock,此函数检查weak_ptr指向的对象是否存在。
如果存在,lock返回一个指向共享对象的shared_ptr,如果不存在,lock将返回一个空指针

weak_ptr的特点
它可以默认初始化,默认初始化是一个空指针
它的直接初始化需要用到shared_ptr,初始化之后将会和该shared_ptr指向同一个对象,但是该shared_ptr的引用计数不会增加
weak_ptr不能直接解引用来访问它所指向的对象,因为weak_ptr不管理指向对象的动态内存,该内存仍由与它直接初始化的shared_ptr管理,
所以有可能会因为shared_ptr的引用计数为0而释放掉动态内存,所以weak_ptr不能解引用来访问对象

*/
void weak_ptr_use1()
{
	cout << "weak_ptr_use1 start";
	auto sptr = std::make_shared(string("I have a dream!"));

	//默认初始化,空指针
	std::weak_ptr nullp;

	//直接初始化
	auto wptr = std::weak_ptr(sptr);

	//允许使用shared_ptr和weak_ptr进行赋值,但是都不会增加和其共享的shared_ptr的引用计数
	std::weak_ptr wptr2 = wptr;
	std::weak_ptr wptr3 = sptr;

	//置空wptr2
	wptr2.reset();

	//输出和wptr3共享对象的shared_ptr的引用计数,这里是1
	cout << "wptr3->use_count=" << wptr3.use_count() << endl;

	//lock返回wptr共享对象的shared_ptr,如果该shared_ptr指向的对象被释放,则返回空shared_ptr
	shared_ptr sptr2 = wptr.lock();

	//此处可以正常输出"I have a dream!"
	if (sptr2) cout << "*sptr2=" << *sptr2 << endl;

	cout << "weak_ptr_use1 end";
}


//第四章 算法
/*
C++标准模板库中包括70多个算法

其中包括查找算法,排序算法,消除算法,记数算法,比较算法,变换算法,置换算法和容器管理等等

这些算法的一个最重要的特性就是它们的统一性,并且可以广泛用于不同的对象和内置的数据类型

STL 中算法大致分为四类:
非可变序列算法:指不直接修改其所操作的容器内容的算法。
可变序列算法:指可以修改它们所操作的容器内容的算法。
排序算法:包括对序列进行排序和合并的算法、搜索算法以及有序序列上的集合操作。
数值算法:对容器内容进行数值计算。
细致分类可分为 13 类,由于算法过多,所以不一一做介绍,只选取几个最常用的算法介绍。

*/

/*
查找算法
查找算法共13个,包含在头文件中,用来提供元素排序策略,这里只列出一部分算法:
adjacent_find: 在iterator对标识元素范围内,查找一对相邻重复元素,找到则返回指向这对元素的第一个元素的ForwardIterator。否则返回last。重载版本使用输入的二元操作符代替相等的判断。
count: 利用等于操作符,把标志范围内的元素与输入值比较,返回相等元素个数。
count_if: 利用输入的操作符,对标志范围内的元素进行操作,返回结果为true的个数。
binary_search: 在有序序列中查找value,找到返回true。重载的版本实用指定的比较函数对象或函数指针来判断相等。
equal_range: 功能类似equal,返回一对iterator,第一个表示lower_bound,第二个表示upper_bound。
find: 利用底层元素的等于操作符,对指定范围内的元素与输入值进行比较。当匹配时,结束搜索,返回指向该元素的Iterator。
find_if: 使用输入的函数代替等于操作符执行find。
search: 给出两个范围,返回一个ForwardIterator,查找成功指向第一个范围内第一次出现子序列(第二个范围)的位置,查找失败指向last1。重载版本使用自定义的比较操作。
search_n: 在指定范围内查找val出现n次的子序列。重载版本使用自定义的比较操作。

*/
#include 
void algorithm_use1()
{
	cout << "algorithm_use1 start";
	vector iv{ 0, 1, 2, 3, 4, 5, 6, 6, 6, 7, 8 };

	/*** adjacent_find: 在iterator对标识元素范围内,查找一对相邻重复元素 ***/
	// 原型: _FwdIt adjacent_find(_FwdIt _First, _FwdIt _Last)
	cout << "adjacent_find: ";
	cout << *adjacent_find(iv.begin(), iv.end()) << endl;

	/*** count: 利用等于操作符,把标志范围内的元素与输入值比较,返回相等元素个数。 ***/
	// 原型: count(_InIt _First, _InIt _Last, const _Ty& _Val)
	cout << "count(==7): ";
	cout << count(iv.begin(), iv.end(), 6) << endl;// 统计6的个数

	/*** count_if: 利用输入的操作符,对标志范围内的元素进行操作,返回结果为true的个数。 ***/
	// 原型: count_if(_InIt _First, _InIt _Last, _Pr _Pred)
	// 统计小于7的元素的个数 :9个
	cout << "count_if(<7): ";
	cout << count_if(iv.begin(), iv.end(), bind2nd(less(), 7)) << endl;

	/*** binary_search: 在有序序列中查找value,找到返回true。 ***/
	// 原型: bool binary_search(_FwdIt _First, _FwdIt _Last, const _Ty& _Val)
	cout << "binary_search: ";
	cout << binary_search(iv.begin(), iv.end(), 4) << endl; // 找到返回true

	/*** equal_range: 功能类似equal,返回一对iterator,第一个表示lower_bound,第二个表示upper_bound。 ***/
	// 原型: equal_range(_FwdIt _First, _FwdIt _Last, const _Ty& _Val)
	pair::iterator, vector::iterator> pairIte;
	pairIte = equal_range(iv.begin(), iv.end(), 3);
	cout << "pairIte.first:" << *(pairIte.first) << endl;// lowerbound 3   
	cout << "pairIte.second:" << *(pairIte.second) << endl; // upperbound 4

	/*** find: 利用底层元素的等于操作符,对指定范围内的元素与输入值进行比较。 ***/
	// 原型: _InIt find(_InIt _First, _InIt _Last, const _Ty& _Val)
	cout << "find: ";
	cout << *find(iv.begin(), iv.end(), 4) << endl; // 返回元素为4的元素的下标位置

	/*** find_if: 使用输入的函数代替等于操作符执行find。 ***/
	// 原型: _InIt find_if(_InIt _First, _InIt _Last, _Pr _Pred)
	cout << "find_if: " << *find_if(iv.begin(), iv.end(), bind2nd(greater(), 2)) << endl; // 返回大于2的第一个元素的位置:3 

	/*** search: 给出两个范围,返回一个ForwardIterator,查找成功指向第一个范围内第一次出现子序列的位置。 ***/
	// 原型: _FwdIt1 search(_FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2, _FwdIt2 _Last2)
	// 在iv中查找 子序列 2 3 第一次出现的位置的元素   
	int iarr3[3] = { 2, 3 };
	vector iv3(iarr3, iarr3 + 2);
	cout << "search: " << *search(iv.begin(), iv.end(), iv3.begin(), iv3.end()) << endl;

	/*** search_n: 在指定范围内查找val出现n次的子序列。 ***/
	// 原型: _FwdIt1 search_n(_FwdIt1 _First1, _FwdIt1 _Last1, _Diff2 _Count, const _Ty& _Val)
	// 在iv中查找 2个6 出现的第一个位置的元素   
	cout << "search_n: " << *search_n(iv.begin(), iv.end(), 2, 6) << endl;

	/*
	adjacent_find: 6
	count(==7): 3
	count_if(<7): 9
	binary_search: 1
	pairIte.first:3
	pairIte.second:4
	find: 4
	find_if: 3
	search: 2
	search_n: 6
	*/

	cout << "algorithm_use1 end";
}




/*
排序和通用算法
排序算法共14个,包含在头文件中,用来判断容器中是否包含某个值,这里只列出一部分算法:
merge: 合并两个有序序列,存放到另一个序列。重载版本使用自定义的比较。
random_shuffle: 对指定范围内的元素随机调整次序。重载版本输入一个随机数产生操作。
nth_element: 将范围内的序列重新排序,使所有小于第n个元素的元素都出现在它前面,而大于它的都出现在后面。重载版本使用自定义的比较操作。
reverse: 将指定范围内元素重新反序排序。
sort: 以升序重新排列指定范围内的元素。重载版本使用自定义的比较操作。
stable_sort: 与sort类似,不过保留相等元素之间的顺序关系。
*/
template 
struct display
{
	void operator()(const T&x) const
	{
		cout << x << " ";
	}
};
// 如果想从大到小排序,可以采用先排序后反转的方式,也可以采用下面方法:
// 自定义从大到小的比较器,用来改变排序方式
bool Comp(const int& a, const int& b) {
	return a > b;
}
void algorithm_use2()
{
	cout << "algorithm_use2 start";
	int iarr1[] = { 0, 1, 2, 3, 4, 5, 6, 6, 6, 7, 8 };
	vector iv1(iarr1, iarr1 + sizeof(iarr1) / sizeof(int));
	vector iv2(iarr1 + 4, iarr1 + 8); // 4 5 6 6
	vector iv3(15);

	/*** merge: 合并两个有序序列,存放到另一个序列 ***/
	// iv1和iv2合并到iv3中(合并后会自动排序)
	merge(iv1.begin(), iv1.end(), iv2.begin(), iv2.end(), iv3.begin());
	cout << "merge合并后: ";
	for_each(iv3.begin(), iv3.end(), display());
	cout << endl;

	/*** random_shuffle: 对指定范围内的元素随机调整次序。 ***/
	int iarr2[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
	vector iv4(iarr2, iarr2 + sizeof(iarr2) / sizeof(int));
	// 打乱顺序  
	random_shuffle(iv4.begin(), iv4.end());
	cout << "random_shuffle打乱后: ";
	for_each(iv4.begin(), iv4.end(), display());
	cout << endl;

	/*** nth_element: 将范围内的序列重新排序。 ***/
	// 将小于iv.begin+5的放到左边   
	nth_element(iv4.begin(), iv4.begin() + 5, iv4.end());
	cout << "nth_element重新排序后: ";
	for_each(iv4.begin(), iv4.end(), display());
	cout << endl;

	/*** reverse: 将指定范围内元素重新反序排序。 ***/
	reverse(iv4.begin(), iv4.begin());
	cout << "reverse翻转后: ";
	for_each(iv4.begin(), iv4.end(), display());
	cout << endl;

	/*** sort: 以升序重新排列指定范围内的元素。 ***/
	// sort(iv4.begin(), iv4.end(), Comp); // 也可以使用自定义Comp()函数
	sort(iv4.begin(), iv4.end(), greater());
	cout << "sort排序(倒序): ";
	for_each(iv4.begin(), iv4.end(), display());
	cout << endl;

	/*** stable_sort: 与sort类似,不过保留相等元素之间的顺序关系。 ***/
	int iarr3[] = { 0, 1, 2, 3, 3, 4, 4, 5, 6 };
	vector iv5(iarr3, iarr3 + sizeof(iarr3) / sizeof(int));
	stable_sort(iv5.begin(), iv5.end(), greater());
	cout << "stable_sort排序(倒序): ";
	for_each(iv5.begin(), iv5.end(), display());
	cout << endl;

	/*
	merge合并后: 0 1 2 3 4 4 5 5 6 6 6 6 6 7 8
	random_shuffle打乱后: 8 1 6 2 0 5 7 3 4
	nth_element重新排序后: 0 1 2 3 4 5 6 7 8
	reverse翻转后: 0 1 2 3 4 5 6 7 8
	sort排序(倒序): 8 7 6 5 4 3 2 1 0
	stable_sort排序(倒序): 6 5 4 4 3 3 2 1 0
	*/

	cout << "algorithm_use2 end";
}



/*
sort()排序函数用法详解
对容器或普通数组中 [first, last) 范围内的元素进行排序,默认进行升序排序。
sort() 函数是基于快速排序实现的

需要注意的是,sort() 函数受到底层实现方式的限制,它仅适用于普通数组和部分类型的容器。换句话说,只有普通数组和具备以下条件的容器,才能使用 sort() 函数:
1.容器支持的迭代器类型必须为随机访问迭代器。这意味着,sort() 只对 array、vector、deque 这 3 个容器提供支持。
2.如果对容器中指定区域的元素做默认升序排序,则元素类型必须支持<小于运算符;同样,如果选用标准库提供的其它排序规则,元素类型也必须支持该规则底层实现所用的比较运算符;
3.sort() 函数在实现排序时,需要交换容器中元素的存储位置。这种情况下,如果容器中存储的是自定义的类对象,则该类的内部必须提供移动构造函数和移动赋值运算符。

另外还需要注意的一点是,对于指定区域内值相等的元素,sort() 函数无法保证它们的相对位置不发生改变。
实际场景中,如果需要保证值相等元素的相对位置不发生改变,可以选用 stable_sort() 排序函数。

sort() 函数有 2 种用法,其语法格式分别为:
//对 [first, last) 区域内的元素做默认的升序排序
void sort (RandomAccessIterator first, RandomAccessIterator last);
//按照指定的 comp 排序规则,对 [first, last) 区域内的元素进行排序
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);

其中,first 和 last 都为随机访问迭代器,它们的组合 [first, last) 用来指定要排序的目标区域;另外在第 2 种格式中,comp 可以是 C++ STL 标准库提供的排序规则(比如 std::greater),也可以是自定义的排序规则。
*/

//以普通函数的方式实现自定义排序规则
bool mycomp(int i, int j) {
	return (i < j);
}
//以函数对象的方式实现自定义排序规则
class mycomp2 {
public:
	bool operator() (int i, int j) {
		return (i < j);
	}
};


void sort_use1()
{
	cout << "sort_use1 start";
	std::vector myvector{ 32, 71, 12, 45, 26, 80, 53, 33 };
	//调用第一种语法格式,对 32、71、12、45 进行排序
	std::sort(myvector.begin(), myvector.begin() + 4); //(12 32 45 71) 26 80 53 33
	//调用第二种语法格式,利用STL标准库提供的其它比较规则(比如 greater)进行排序
	std::sort(myvector.begin(), myvector.begin() + 4, std::greater()); //(71 45 32 12) 26 80 53 33

	//调用第二种语法格式,通过自定义比较规则进行排序
	std::sort(myvector.begin(), myvector.end(), mycomp);//12 26 32 33 45 53 71 80
	std::sort(myvector.begin(), myvector.end(), mycomp2());//12 26 32 33 45 53 71 80
	//输出 myvector 容器中的元素
	for (std::vector::iterator it = myvector.begin(); it != myvector.end(); ++it) {
		std::cout << *it << ' ';
	}
	cout << "sort_use1 end";
}
/*
可以看到,程序中分别以函数和函数对象的方式自定义了具有相同功能的 mycomp 和 mycomp2 升序排序规则。
需要注意的是,和为关联式容器设定排序规则不同,给 sort() 函数指定排序规则时,需要为其传入一个函数名(例如 mycomp )或者函数对象(例如 std::greater() 或者 mycomp2())。
那么,sort() 函数的效率怎样?该函数实现排序的平均时间复杂度为N*log2N(其中 N 为指定区域 [first, last) 中 last 和 first 的距离)。
*/

/*
stable_sort()用法详解
和 sort() 函数功能相似,不同之处在于,对于 [first, last) 范围内值相同的元素,该函数不会改变它们的相对位置。
既要完成排序又要保证相等元素的相对位置,可以使用 stable_sort() 函数。

partial_sort (first, middle, last)
从 [first,last) 范围内,筛选出 muddle-first 个最小的元素并排序存放在 [first,middle) 区间中。

partial_sort_copy (first, last, result_first, result_last)
从 [first, last) 范围内筛选出 result_last-result_first 个元素排序并存储到 [result_first, result_last) 指定的范围中。

is_sorted (first, last)
检测 [first, last) 范围内是否已经排好序,默认检测是否按升序排序。

is_sorted_until (first, last)
和 is_sorted() 函数功能类似,唯一的区别在于,如果 [first, last) 范围的元素没有排好序,则该函数会返回一个指向首个不遵循排序规则的元素的迭代器。

*/

/*
删除和替换算法
删除和替换算法共15个,包含在头文件中,这里只列出一部分算法:
copy: 复制序列。
copy_backward: 与copy相同,不过元素是以相反顺序被拷贝。
remove: 删除指定范围内所有等于指定元素的元素。注意,该函数不是真正删除函数。内置函数不适合使用remove和remove_if函数。
remove_copy: 将所有不匹配元素复制到一个制定容器,返回OutputIterator指向被拷贝的末元素的下一个位置。
remove_if: 删除指定范围内输入操作结果为true的所有元素。
remove_copy_if: 将所有不匹配元素拷贝到一个指定容器。

replace: 将指定范围内所有等于vold的元素都用vnew代替。
replace_copy: 与replace类似,不过将结果写入另一个容器。
replace_if: 将指定范围内所有操作结果为true的元素用新值代替。
replace_copy_if: 与replace_if,不过将结果写入另一个容器。
swap: 交换存储在两个对象中的值。
swap_range: 将指定范围内的元素与另一个序列元素值进行交换。
unique: 清除序列中重复元素,和remove类似,它也不能真正删除元素。重载版本使用自定义比较操作。
unique_copy: 与unique类似,不过把结果输出到另一个容器。

*/
void algorithm_use3()
{
	cout << "algorithm_use3 start";
	int iarr1[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
	vector iv1(iarr1, iarr1 + sizeof(iarr1) / sizeof(int));
	vector iv2(9);

	/*** copy: 复制序列 ***/
	//  原型: _OutIt copy(_InIt _First, _InIt _Last,_OutIt _Dest)
	copy(iv1.begin(), iv1.end(), iv2.begin());
	cout << "copy(iv2): ";
	for_each(iv2.begin(), iv2.end(), display());
	cout << endl;

	/*** copy_backward: 与copy相同,不过元素是以相反顺序被拷贝。 ***/
	//  原型: _BidIt2 copy_backward(_BidIt1 _First, _BidIt1 _Last,_BidIt2 _Dest)
	copy_backward(iv1.begin(), iv1.end(), iv2.rend());
	cout << "copy_backward(iv2): ";
	for_each(iv2.begin(), iv2.end(), display());
	cout << endl;

	/*** remove: 删除指定范围内所有等于指定元素的元素。 ***/
	//  原型: _FwdIt remove(_FwdIt _First, _FwdIt _Last, const _Ty& _Val)
	remove(iv1.begin(), iv1.end(), 5); // 删除元素5
	cout << "remove(iv1): ";
	for_each(iv1.begin(), iv1.end(), display());
	cout << endl;

	/*** remove_copy: 将所有不匹配元素复制到一个制定容器,返回OutputIterator指向被拷贝的末元素的下一个位置。 ***/
	//  原型: 	_OutIt remove_copy(_InIt _First, _InIt _Last,_OutIt _Dest, const _Ty& _Val)
	vector iv3(8);
	remove_copy(iv1.begin(), iv1.end(), iv3.begin(), 4); // 去除4 然后将一个容器的元素复制到另一个容器
	cout << "remove_copy(iv3): ";
	for_each(iv3.begin(), iv3.end(), display());
	cout << endl;

	/*** remove_if: 删除指定范围内输入操作结果为true的所有元素。 ***/
	//  原型: _FwdIt remove_if(_FwdIt _First, _FwdIt _Last, _Pr _Pred)
	remove_if(iv3.begin(), iv3.end(), bind2nd(less(), 6)); //  将小于6的元素 "删除"
	cout << "remove_if(iv3): ";
	for_each(iv3.begin(), iv3.end(), display());
	cout << endl;

	/*** remove_copy_if: 将所有不匹配元素拷贝到一个指定容器。 ***/
	// 原型: _OutIt remove_copy_if(_InIt _First, _InIt _Last,_OutIt _Dest, _Pr _Pred)
	//  将iv1中小于6的元素 "删除"后,剩下的元素再复制给iv3
	remove_copy_if(iv1.begin(), iv1.end(), iv2.begin(), bind2nd(less(), 4));
	cout << "remove_if(iv2): ";
	for_each(iv2.begin(), iv2.end(), display());
	cout << endl;

	/*
	copy(iv2): 0 1 2 3 4 5 6 7 8
	copy_backward(iv2): 8 7 6 5 4 3 2 1 0
	remove(iv1): 0 1 2 3 4 6 7 8 8
	remove_copy(iv3): 0 1 2 3 6 7 8 8
	remove_if(iv3): 6 7 8 8 6 7 8 8
	remove_if(iv2): 4 6 7 8 8 3 2 1 0
	*/
	cout << "algorithm_use3 end";
}

void algorithm_use4()
{
	cout << "algorithm_use4 start";
	int iarr[] = { 8, 10, 7, 8, 6, 6, 7, 8, 6, 7, 8 };
	vector iv(iarr, iarr + sizeof(iarr) / sizeof(int));

	/*** replace: 将指定范围内所有等于vold的元素都用vnew代替。 ***/
	//  原型: void replace(_FwdIt _First, _FwdIt _Last, const _Ty& _Oldval, const _Ty& _Newval)
	// 将容器中6 替换为 3   
	replace(iv.begin(), iv.end(), 6, 3);
	cout << "replace(iv): ";
	for_each(iv.begin(), iv.end(), display()); // 由于_X是static 所以接着 增长
	cout << endl; // iv:8 10 7 8 3 3 7 8 3 7 8   

	/*** replace_copy: 与replace类似,不过将结果写入另一个容器。 ***/
	//  原型: _OutIt replace_copy(_InIt _First, _InIt _Last, _OutIt _Dest, const _Ty& _Oldval, const _Ty& _Newval)
	vector iv2(12);
	// 将容器中3 替换为 5,并将结果写入另一个容器。  
	replace_copy(iv.begin(), iv.end(), iv2.begin(), 3, 5);
	cout << "replace_copy(iv2): ";
	for_each(iv2.begin(), iv2.end(), display());
	cout << endl; // iv2:8 10 7 8 5 5 7 8 5 7 8 0(最后y一个残留元素)   

	/*** replace_if: 将指定范围内所有操作结果为true的元素用新值代替。 ***/
	//  原型: void replace_if(_FwdIt _First, _FwdIt _Last, _Pr _Pred, const _Ty& _Val)
	// 将容器中小于 5 替换为 2   
	replace_if(iv.begin(), iv.end(), bind2nd(less(), 5), 2);
	cout << "replace_copy(iv): ";
	for_each(iv.begin(), iv.end(), display());
	cout << endl; // iv:8 10 7 8 2 5 7 8 2 7 8   

	/*** replace_copy_if: 与replace_if,不过将结果写入另一个容器。 ***/
	//  原型: _OutIt replace_copy_if(_InIt _First, _InIt _Last, _OutIt _Dest, _Pr _Pred, const _Ty& _Val)
	// 将容器中小于 5 替换为 2,并将结果写入另一个容器。  
	replace_copy_if(iv.begin(), iv.end(), iv2.begin(), bind2nd(equal_to(), 8), 9);
	cout << "replace_copy_if(iv2): ";
	for_each(iv2.begin(), iv2.end(), display());
	cout << endl; // iv2:9 10 7 8 2 5 7 9 2 7 8 0(最后一个残留元素)

	int iarr3[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, };
	vector iv3(iarr3, iarr3 + sizeof(iarr3) / sizeof(int));
	int iarr4[] = { 8, 10, 7, 8, 6, 6, 7, 8, 6, };
	vector iv4(iarr4, iarr4 + sizeof(iarr4) / sizeof(int));

	/*** swap: 交换存储在两个对象中的值。 ***/
	//  原型: _OutIt replace_copy_if(_InIt _First, _InIt _Last, _OutIt _Dest, _Pr _Pred, const _Ty& _Val)
	// 将两个容器中的第一个元素交换  
	swap(*iv3.begin(), *iv4.begin());
	cout << "swap(iv3): ";
	for_each(iv3.begin(), iv3.end(), display());
	cout << endl;

	/*** swap_range: 将指定范围内的元素与另一个序列元素值进行交换。 ***/
	//  原型: _FwdIt2 swap_ranges(_FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _Dest)
	// 将两个容器中的全部元素进行交换  
	swap_ranges(iv4.begin(), iv4.end(), iv3.begin());
	cout << "swap_range(iv3): ";
	for_each(iv3.begin(), iv3.end(), display());
	cout << endl;

	/*** unique: 清除序列中相邻的重复元素,和remove类似,它也不能真正删除元素。 ***/
	//  原型: _FwdIt unique(_FwdIt _First, _FwdIt _Last, _Pr _Pred) 
	unique(iv3.begin(), iv3.end());
	cout << "unique(iv3): ";
	for_each(iv3.begin(), iv3.end(), display());
	cout << endl;

	/*** unique_copy: 与unique类似,不过把结果输出到另一个容器。 ***/
	//  原型: _OutIt unique_copy(_InIt _First, _InIt _Last, _OutIt _Dest, _Pr _Pred)
	unique_copy(iv3.begin(), iv3.end(), iv4.begin());
	cout << "unique_copy(iv4): ";
	for_each(iv4.begin(), iv4.end(), display());
	cout << endl;

	/*
	replace(iv): 8 10 7 8 3 3 7 8 3 7 8
	replace_copy(iv2): 8 10 7 8 5 5 7 8 5 7 8 0
	replace_copy(iv): 8 10 7 8 2 2 7 8 2 7 8
	replace_copy_if(iv2): 9 10 7 9 2 2 7 9 2 7 9 0
	swap(iv3): 8 1 2 3 4 5 6 7 8
	swap_range(iv3): 0 10 7 8 6 6 7 8 6
	unique(iv3): 0 10 7 8 6 7 8 6 6
	unique_copy(iv4): 0 10 7 8 6 7 8 6 8
	*/
	cout << "algorithm_use4 end";
}

/*
排列组合算法
排列组合算法共2个,包含在头文件中,用来提供计算给定集合按一定顺序的所有可能排列组合,这里全部列出:
next_permutation: 取出当前范围内的排列,并重新排序为下一个字典序排列。重载版本使用自定义的比较操作。
prev_permutation: 取出指定范围内的序列并将它重新排序为上一个字典序排列。如果不存在上一个序列则返回false。重载版本使用自定义的比较操作。
*/

void algorithm_use5()
{
	cout << "algorithm_use5 start";
	int iarr[] = { 12, 17, 20, 22, 23, 30, 33, 40 };
	vector iv(iarr, iarr + sizeof(iarr) / sizeof(int));

	/*** next_permutation: 取出当前范围内的排列,并重新排序为下一个字典序排列。***/
	//  原型: bool next_permutation(_BidIt _First, _BidIt _Last)
	// 生成下一个排列组合(字典序)   
	next_permutation(iv.begin(), iv.end());
	for_each(iv.begin(), iv.end(), display());
	cout << endl;

	/*** prev_permutation: 取出指定范围内的序列并将它重新排序为上一个字典序排列。 ***/
	//  原型: bool prev_permutation(_BidIt _First, _BidIt _Last)
	prev_permutation(iv.begin(), iv.end());
	for_each(iv.begin(), iv.end(), display());
	cout << endl;

	/*
	12 17 20 22 23 30 40 33
	12 17 20 22 23 30 33 40
	*/
	cout << "algorithm_use5 end";
}

/*
数值算法
数值算法共4个,包含在头文件中,分别是:
accumulate: iterator对标识的序列段元素之和,加到一个由val指定的初始值上。重载版本不再做加法,而是传进来的二元操作符被应用到元素上。
partial_sum: 创建一个新序列,其中每个元素值代表指定范围内该位置前所有元素之和。重载版本使用自定义操作代替加法。
inner_product: 对两个序列做内积(对应元素相乘,再求和)并将内积加到一个输入的初始值上。重载版本使用用户定义的操作。
adjacent_difference: 创建一个新序列,新序列中每个新值代表当前元素与上一个元素的差。重载版本用指定二元操作计算相邻元素的差。

*/
#include 
void algorithm_use6()
{
	cout << "algorithm_use6 start";
	int arr[] = { 1, 2, 3, 4, 5 };
	vector vec(arr, arr + 5);
	vector vec2(arr, arr + 5);

	//  accumulate: iterator对标识的序列段元素之和,加到一个由val指定的初始值上。
	int temp;
	int val = 0;
	temp = accumulate(vec.begin(), vec.end(), val);
	cout << "accumulate(val = 0): " << temp << endl;
	val = 1;
	temp = accumulate(vec.begin(), vec.end(), val);
	cout << "accumulate(val = 1): " << temp << endl;

	// inner_product: 对两个序列做内积(对应元素相乘,再求和)并将内积加到一个输入的初始值上。
	// 这里是:1*1 + 2*2 + 3*3 + 4*4 + 5*5
	val = 0;
	temp = inner_product(vec.begin(), vec.end(), vec2.begin(), val);
	cout << "inner_product(val = 0): " << temp << endl;

	// partial_sum: 创建一个新序列,其中每个元素值代表指定范围内该位置前所有元素之和。
	// 第一次,1   第二次,1+2  第三次,1+2+3  第四次,1+2+3+4
	ostream_iterator oit(cout, " "); // 迭代器绑定到cout上作为输出使用
	cout << "ostream_iterator: ";
	partial_sum(vec.begin(), vec.end(), oit);// 依次输出前n个数的和
	cout << endl;
	// 第一次,1   第二次,1-2  第三次,1-2-3  第四次,1-2-3-4
	cout << "ostream_iterator(minus): ";
	partial_sum(vec.begin(), vec.end(), oit, minus());// 依次输出第一个数减去(除第一个数外到当前数的和)
	cout << endl;

	// adjacent_difference: 创建一个新序列,新序列中每个新值代表当前元素与上一个元素的差。
	// 第一次,1-0   第二次,2-1  第三次,3-2  第四次,4-3
	cout << "adjacent_difference: ";
	adjacent_difference(vec.begin(), vec.end(), oit); // 输出相邻元素差值 后面-前面
	cout << endl;
	// 第一次,1+0   第二次,2+1  第三次,3+2  第四次,4+3
	cout << "adjacent_difference(plus): ";
	adjacent_difference(vec.begin(), vec.end(), oit, plus()); // 输出相邻元素差值 后面-前面 
	cout << endl;

	/*
	accumulate(val = 0): 15
	accumulate(val = 1): 16
	inner_product(val = 0): 55
	ostream_iterator: 1 3 6 10 15
	ostream_iterator(minus): 1 -1 -4 -8 -13
	adjacent_difference: 1 1 1 1 1
	adjacent_difference(plus): 1 3 5 7 9
	*/
	cout << "algorithm_use6 end";
}
/*
生成和异变算法
生成和异变算法共6个,包含在头文件中,这里只列出一部分算法:
fill: 将输入值赋给标志范围内的所有元素。
fill_n: 将输入值赋给first到first+n范围内的所有元素。
for_each: 用指定函数依次对指定范围内所有元素进行迭代访问,返回所指定的函数类型。该函数不得修改序列中的元素。
generate: 连续调用输入的函数来填充指定的范围。
generate_n: 与generate函数类似,填充从指定iterator开始的n个元素。
transform: 将输入的操作作用与指定范围内的每个元素,并产生一个新的序列。重载版本将操作作用在一对元素上,另外一个元素来自输入的另外一个序列。结果输出到指定容器。

*/
//  作用类似于上面结构体,只不过只能显示int类型的数据
void printElem(int& elem)
{
	cout << elem << " ";
}

template
struct plus2
{
	void operator()(T&x)const
	{
		x += 2;
	}

};

class even_by_two
{
private:
	static int _x; //  注意静态变量   
public:
	int operator()()const
	{
		return _x += 2;
	}
};
int even_by_two::_x = 0; //  初始化静态变量

void algorithm_use7()
{
	cout << "algorithm_use7 start";
	int iarr[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
	vector iv(iarr, iarr + sizeof(iarr) / sizeof(int));

	/*** fill: 将输入值赋给标志范围内的所有元素。 ***/
	//  原型: void fill(_FwdIt _First, _FwdIt _Last, const _Ty& _Val)  
	fill(iv.begin(), iv.end(), 5);
	cout << "fill: ";
	for_each(iv.begin(), iv.end(), display());
	cout << endl;

	/*** fill_n: 将输入值赋给first到first+n范围内的所有元素。 ***/
	//  原型: _OutIt fill_n(_OutIt _Dest, _Diff _Count, const _Ty& _Val)
	fill_n(iv.begin(), 4, 3); //  赋4个3给iv 
	cout << "fill_n: ";
	for_each(iv.begin(), iv.end(), display());
	cout << endl;

	/*** for_each: 用指定函数依次对指定范围内所有元素进行迭代访问,返回所指定的函数类型。 ***/
	//  原型: _Fn1 for_each(_InIt _First, _InIt _Last, _Fn1 _Func)
	for_each(iv.begin(), iv.end(), plus2()); //  每个元素+2
	cout << "for_each: ";
	for_each(iv.begin(), iv.end(), printElem); //  输出
	cout << endl;

	/*** generate: 连续调用输入的函数来填充指定的范围。 ***/
	//  原型: void generate(_FwdIt _First, _FwdIt _Last, _Fn0 _Func)
	//  使用even_by_two()函数返回的值,来填充容器iv
	generate(iv.begin(), iv.end(), even_by_two());
	cout << "generate: ";
	for_each(iv.begin(), iv.end(), display());
	cout << endl;

	/*** generate_n: 与generate函数类似,填充从指定iterator开始的n个元素。 ***/
	//  原型: _OutIt generate_n(_OutIt _Dest, _Diff _Count, _Fn0 _Func)
	//  使用even_by_two()函数返回的值,来填充容器iv的前三个值
	generate_n(iv.begin(), 3, even_by_two());
	cout << "generate_n: ";
	for_each(iv.begin(), iv.end(), display()); //  由于_X是static 所以接着 增长
	cout << endl;

	/*** transform: 将输入的操作作用与指定范围内的每个元素,并产生一个新的序列。 ***/
	//  原型: _OutIt transform(_InIt _First, _InIt _Last, _OutIt _Dest, _Fn1 _Func)
	// 容器的所有值全部减2
	transform(iv.begin(), iv.end(), iv.begin(), bind2nd(minus(), 2));
	cout << "transform: ";
	for_each(iv.begin(), iv.end(), display()); //  由于_X是static 所以接着 增长
	cout << endl;

	/*
	fill: 5 5 5 5 5 5 5 5 5
	fill_n: 3 3 3 3 5 5 5 5 5
	for_each: 5 5 5 5 7 7 7 7 7
	generate: 2 4 6 8 10 12 14 16 18
	generate_n: 20 22 24 8 10 12 14 16 18
	transform: 18 20 22 6 8 10 12 14 16
	*/
	cout << "algorithm_use7 end";
}
/*
关系算法
关系算法共8个,包含在头文件中,这里只列出一部分算法:
equal: 如果两个序列在标志范围内元素都相等,返回true。重载版本使用输入的操作符代替默认的等于操作符。
includes: 判断第一个指定范围内的所有元素是否都被第二个范围包含,使用底层元素的<操作符,成功返回true。重载版本使用用户输入的函数。
max: 返回两个元素中较大一个。重载版本使用自定义比较操作。
min: 返回两个元素中较小一个。重载版本使用自定义比较操作。
max_element: 返回一个ForwardIterator,指出序列中最大的元素。重载版本使用自定义比较操作。
min_element: 返回一个ForwardIterator,指出序列中最小的元素。重载版本使用自定义比较操作。
mismatch: 并行比较两个序列,指出第一个不匹配的位置,返回一对iterator,标志第一个不匹配元素位置。如果都匹配,返回每个容器的last。重载版本使用自定义的比较操作。

*/
void algorithm_use8()
{
	cout << "algorithm_use8 start";
	int iarr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	vector iv1(iarr, iarr + 5);
	vector iv2(iarr, iarr + 9);

	//  equal: 如果两个序列在标志范围内元素都相等,返回true。
	cout << "equal: " << equal(iv1.begin(), iv1.end(), iv2.begin()) << endl;//  1 表示相等,因为只比较跟 iv1长度大小的数组      

	// includes: 判断第一个指定范围内的所有元素是否都被第二个范围包含,使用底层元素的<操作符,成功返回true。
	// 判断判断iv2中元素是否都出现在iv1中
	cout << "includes: " << includes(iv1.begin(), iv1.end(), iv2.begin(), iv2.end()) << endl;

	// max: 返回两个元素中较大一个。
	cout << "max: " << max(iv1[0], iv1[1]) << endl;
	// min: 返回两个元素中较小一个。
	cout << "min: " << min(iv1[0], iv1[1]) << endl;

	// max_element: 返回一个ForwardIterator,指出序列中最大的元素。
	cout << "max_element: " << *max_element(iv1.begin(), iv1.end()) << endl;
	// min_element: 返回一个ForwardIterator,指出序列中最小的元素。
	cout << "min_element: " << *min_element(iv1.begin(), iv1.end()) << endl;

	//  mismatch: 并行比较两个序列,指出第一个不匹配的位置,返回一对iterator,标志第一个不匹配元素位置。如果都匹配,返回每个容器的last。
	pair::iterator, vector::iterator> pa;
	pa = mismatch(iv1.begin(), iv1.end(), iv2.begin());
	if (pa.first == iv1.end()) //  true 表示相等,因为只比较跟iv1长度大小的数组 
		cout << "第一个向量与第二个向量匹配" << endl;
	else
	{
		cout << "两个向量不同点--第一个向量点:" << *(pa.first) << endl; // 这样写很危险,应该判断是否到达end   
		cout << "两个向量不同点--第二个向量点:" << *(pa.second) << endl;
	}

	/*
	equal: 1
	includes: 0
	max: 2
	min: 1
	max_element: 5
	min_element: 1
	第一个向量与第二个向量匹配
	*/
	cout << "algorithm_use8 end";
}
/*
集合算法
集合算法共4个,包含在头文件中,这里全部列出:
set_union: 构造一个有序序列,包含两个序列中所有的不重复元素。重载版本使用自定义的比较操作。
set_intersection: 构造一个有序序列,其中元素在两个序列中都存在。重载版本使用自定义的比较操作。
set_difference: 构造一个有序序列,该序列仅保留第一个序列中存在的而第二个中不存在的元素。重载版本使用自定义的比较操作。
set_symmetric_difference: 构造一个有序序列,该序列取两个序列的对称差集(并集-交集)。

*/
#include 
void algorithm_use9()
{
	cout << "algorithm_use9 start";
	int iarr1[] = { 1, 3, 5, 7, 9, 11 };
	int iarr2[] = { 1, 1, 2, 3, 5, 8, 13 };

	multiset s1(iarr1, iarr1 + 6);
	multiset s2(iarr2, iarr2 + 7);
	cout << "s1: ";
	for_each(s1.begin(), s1.end(), display());
	cout << endl;
	cout << "s2: ";
	for_each(s2.begin(), s2.end(), display());
	cout << endl;

	/*** set_union: 构造一个有序序列,包含两个序列中所有的不重复元素。 ***/
	//  原型: _OutIt set_union(_InIt1 _First1, _InIt1 _Last1, _InIt2 _First2, _InIt2 _Last2, _OutIt _Dest)
	cout << "union of s1 and s2: ";
	// 两个集合合并,相同元素个数取 max(m,n)。   
	set_union(s1.begin(), s1.end(), s2.begin(), s2.end(), ostream_iterator(cout, " "));
	cout << endl;

	/*** set_intersection: 构造一个有序序列,其中元素在两个序列中都存在。 ***/
	//  原型: _OutIt set_union(_InIt1 _First1, _InIt1 _Last1, _InIt2 _First2, _InIt2 _Last2, _OutIt _Dest)
	cout << "Intersection of s1 and s2: ";
	// 两个集合交集,相同元素个数取 min(m,n).  
	set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(), ostream_iterator(cout, " "));
	cout << endl;

	/*** set_difference: 构造一个有序序列,该序列仅保留第一个序列中存在的而第二个中不存在的元素。 ***/
	//  原型: _OutIt set_union(_InIt1 _First1, _InIt1 _Last1, _InIt2 _First2, _InIt2 _Last2, _OutIt _Dest)
	cout << "Intersection of s1 and s2: ";
	// 两个集合差集 就是去掉S1中 的s2   
	set_difference(s1.begin(), s1.end(), s2.begin(), s2.end(), ostream_iterator(cout, " "));
	cout << endl;

	/*** set_symmetric_difference: 构造一个有序序列,该序列取两个序列的对称差集(并集-交集)。 ***/
	//  原型: _OutIt set_union(_InIt1 _First1, _InIt1 _Last1, _InIt2 _First2, _InIt2 _Last2, _OutIt _Dest)
	cout << "Intersection of s1 and s2: ";
	// 两个集合对称差集:就是取两个集合互相没有的元素 。两个排序区间,元素相等指针后移,不等输出小的并前进   
	// 相同元素的个数 abs(m-n)   
	set_symmetric_difference(s1.begin(), s1.end(), s2.begin(), s2.end(), ostream_iterator(cout, " "));
	cout << endl;

	/*
	s1: 1 3 5 7 9 11
	s2: 1 1 2 3 5 8 13
	union of s1 and s2: 1 1 2 3 5 7 8 9 11 13
	Intersection of s1 and s2: 1 3 5
	Intersection of s1 and s2: 7 9 11
	Intersection of s1 and s2: 1 2 7 8 9 11 13
	*/
	cout << "algorithm_use9 end";
}
/*
堆算法
集合算法共4个,包含在头文件中,这里只列出一部分算法:
make_heap: 把指定范围内的元素生成一个堆。重载版本使用自定义比较操作。
pop_heap: 并不真正把最大元素从堆中弹出,而是重新排序堆。它把first和last-1交换,然后重新生成一个堆。可使用容器的back来访问被"弹出"的元素或者使用pop_back进行真正的删除。重载版本使用自定义的比较操作。
push_heap: 假设first到last-1是一个有效堆,要被加入到堆的元素存放在位置last-1,重新生成堆。在指向该函数前,必须先把元素插入容器后。重载版本使用指定的比较操作。

*/
void algorithm_use10()
{
	cout << "algorithm_use10 start";
	int iarr[] = { 4, 5, 1, 3, 2 };
	vector iv(iarr, iarr + sizeof(iarr) / sizeof(int));

	/*** make_heap: 把指定范围内的元素生成一个堆。 ***/
	//  原型: void make_heap(_RanIt _First, _RanIt _Last)
	make_heap(iv.begin(), iv.end());
	cout << "make_heap: ";
	for_each(iv.begin(), iv.end(), display());
	cout << endl;

	/*** pop_heap: 并不真正把最大元素从堆中弹出,而是重新排序堆。 ***/
	//  原型: void pop_heap(_RanIt _First, _RanIt _Last)
	pop_heap(iv.begin(), iv.end());
	iv.pop_back();
	cout << "pop_heap: ";
	for_each(iv.begin(), iv.end(), display());
	cout << endl;

	/*** push_heap: 假设first到last-1是一个有效堆,要被加入到堆的元素存放在位置last-1,重新生成堆。 ***/
	//  原型: void push_heap(_RanIt _First, _RanIt _Last)
	iv.push_back(6);
	push_heap(iv.begin(), iv.end());
	cout << "push_heap: ";
	for_each(iv.begin(), iv.end(), display());
	cout << endl;

	/*
	make_heap: 5 4 1 3 2
	pop_heap: 4 3 1 2
	push_heap: 6 4 1 2 3
	*/
	cout << "algorithm_use10 end";
}

/*
第五章 numeric
是C++标准程序库中的一个头文件,定义了C++ STL标准中的基础性的数值算法,函数并不多,一共5个,偶有接触,这里一并介绍一下,免得每次网上翻阅查找。

这五个函数分别为:accumulate;adjacent_difference;inner_product;partial_sum;iota。
1. accumulate
功能:以init为初值,对迭代器给出的值序列做累加,返回累加结果值

函数模板
//模板一:默认累加
template 
T accumulate (InputIterator first, InputIterator last, T init);

//模板二:自定义操作--binary_op
template 
T accumulate (InputIterator first, InputIterator last, T init,
BinaryOperation binary_op);

应用举例
*/
int myfunction(int x, int y) { return x + 2 * y; }
struct myclass
{
	int operator()(int x, int y) { return x + 3 * y; }
} myobject;

void numeric_use1()
{
	cout << "numeric_use1 start";
	int init = 100;
	int numbers[] = { 10, 20, 30 };

	std::cout << "using default accumulate: ";//160
	std::cout << std::accumulate(numbers, numbers + 3, init);//init + numbers = 100 + 10 + 20 + 30 = 160
	std::cout << '\n';

	std::cout << "using functional's minus: ";//40
	std::cout << std::accumulate(numbers, numbers + 3, init, std::minus());//init - numbers = 100 - 10 - 20 - 30 = 40
	std::cout << '\n';

	std::cout << "using custom function: ";// 220
	std::cout << std::accumulate(numbers, numbers + 3, init, myfunction);//init (myfunction) numbers
	std::cout << '\n';

	std::cout << "using custom object: ";//280
	std::cout << std::accumulate(numbers, numbers + 3, init, myobject);//init (myobject) numbers 
	std::cout << '\n';

	cout << "numeric_use1 end";
}

/*
adjacent_difference
功能:对输入序列,计算相邻两项的差值(后一个减前一个元素),写入到输出序列(result)中。

函数模板
//模板一:默认形式,相邻差值写入至result中
template 
OutputIterator adjacent_difference (InputIterator first, InputIterator last,
OutputIterator result);

//模板二:自定义操作--binary_op
template 
OutputIterator adjacent_difference ( InputIterator first, InputIterator last,
OutputIterator result, BinaryOperation binary_op );

应用举例

*/
int myop(int x, int y) { return x + y; }
void numeric_use2()
{
	cout << "numeric_use2 start";
	int val[] = { 1, 2, 3, 5, 9, 11, 12 };
	int result[7];

	std::adjacent_difference(val, val + 7, result);//后面减前面的:1 1 1 2 4 2 1
	std::cout << "using default adjacent_difference: ";//1 1 1 2 4 2 1
	for (int i = 0; i < 7; i++)
		std::cout << result[i] << ' ';
	std::cout << '\n';

	std::adjacent_difference(val, val + 7, result, std::multiplies());//std::multiplies():表示乘法
	std::cout << "using functional operation multiplies: ";//1 2 6 15 45 99 132
	for (int i = 0; i < 7; i++)
		std::cout << result[i] << ' ';
	std::cout << '\n';

	std::adjacent_difference(val, val + 7, result, myop);//自定义方法
	std::cout << "using custom function: ";//1 3 5 8 14 20 23
	for (int i = 0; i < 7; i++)
		std::cout << result[i] << ' ';
	std::cout << '\n';

	cout << "numeric_use2 end";
}

/*
inner_product
功能:计算两个输入序列的内积。

函数模板
//模板一:默认模板
template 
T inner_product (InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, T init);

//模板二:自定义操作--binary_op1 binary_op2
template 
T inner_product (InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, T init,
BinaryOperation1 binary_op1,
BinaryOperation2 binary_op2);

应用举例
*/
int myaccumulator(int x, int y) { return x - y; }
int myproduct(int x, int y) { return x + y; }
void numeric_use3()
{
	cout << "numeric_use3 start";
	int init = 100;
	int series1[] = { 10, 20, 30 };
	int series2[] = { 1, 2, 3 };

	std::cout << "using default inner_product: ";//240
	std::cout << std::inner_product(series1, series1 + 3, series2, init);//init = init + (*first1)*(*first2) ==》100 + 10*1 + 20*2 + 30*3
	std::cout << '\n';

	std::cout << "using functional operations: ";//70
	std::cout << std::inner_product(series1, series1 + 3, series2, init,
		std::minus(), std::divides());
	std::cout << '\n';

	std::cout << "using custom functions: ";//34
	std::cout << std::inner_product(series1, series1 + 3, series2, init,
		myaccumulator, myproduct);
	std::cout << '\n';

	cout << "numeric_use3 end";
}
/*
partial_sum
功能:计算局部累加和(每次都加上前面的所有元素),计算结果放入result中。
//模板一:默认计算计算局部累加和
template 
OutputIterator partial_sum (InputIterator first, InputIterator last,
OutputIterator result);

//模板二:自定义操作--binary_op
template 
OutputIterator partial_sum (InputIterator first, InputIterator last,
OutputIterator result, BinaryOperation binary_op);

应用举例
*/
void numeric_use4()
{
	cout << "numeric_use4 start";
	int val[] = { 1, 2, 3, 4, 5 };
	int result[5];

	std::partial_sum(val, val + 5, result);//每次加入前面所有的元素放入result中
	std::cout << "using default partial_sum: ";//1 3 6 10 15
	for (int i = 0; i < 5; i++)
		std::cout << result[i] << ' ';
	std::cout << '\n';

	std::partial_sum(val, val + 5, result, std::multiplies());//每次乘以前面的元素
	std::cout << "using functional operation multiplies: ";//1 2 6 24 120
	for (int i = 0; i < 5; i++)
		std::cout << result[i] << ' ';
	std::cout << '\n';

	std::partial_sum(val, val + 5, result, myop);//自定义操作myop
	std::cout << "using custom function: ";//1 4 8 13 19
	for (int i = 0; i < 5; i++)
		std::cout << result[i] << ' ';
	std::cout << '\n';

	cout << "numeric_use4 end";
}
/*
iota
功能:向序列中写入以val为初值的连续值序列。

函数模板
template 
void iota (ForwardIterator first, ForwardIterator last, T val);

应用举例
*/
void numeric_use5()
{
	cout << "numeric_use5 start";
	int numbers[10];

	std::iota(numbers, numbers + 10, 100);//以100为初值

	for (int &i : numbers)
		std::cout << ' ' << i;//100 101 102 103 104 105 106 107 108 109

	std::cout << '\n';

	cout << "numeric_use5 end";
}

/*
第六章 
模仿函数的类,使用方式如同函数。本质是类中重载括弧运算符operator()。
注意
1,仿函数是一个类,不是函数。
2,因为仿函数重载了"()"操作符,所以它才可以像一个函数一样被调用。
3,仿函数超出了普通函数的概念,拥有自己的状态。
比如可以计算仿函数被调用的次数,不用想普通函数一样需要一个全局变量类计数,对于仿函数来说,可以直接在类中创建一个成员变量num,然后在仿函数中num++。
4,函数对象可以作为函数的参数。
class MyPrint
{
public:
void operator()(int num)
{
cout << num << endl;
}
};

void doPrint(MyPrint myprint,int num)
{
myprint(num);
}

int main()
{
doPrint(MyPrint(), 1000);//传入两个参数,一个是匿名对象,一个是int类型数据。
return 0;
}
5,函数对象(仿函数)通常不定义构造函数和析构函数,避免函数调用的运行时问题。
6,函数对象(仿函数)与普通函数相比,是可以内联编译的(因为仿函数是成员函数,在函数声明的前面会有关键字Inline),
在调用的 时候不会像普通函数那样进行出栈和进栈的操纵,而是直接抛源码,效率更高。
7,模板函数对象使得函数对象根据有通用性(在<>中函数对象的类可以当作数据类型传入)

C语言的处理方式
使用函数指针和回调函数来实现代码复用。例如qsort()
#include 
#include 
int arr_sort( const void *a, const void *b) {
return *(int*)a-*(int*)b;
}
int main() {
int arr[5] = { 4, 1, 2, 5, 6 };
qsort(arr, 5, sizeof(arr[0]), arr_sort);
int  i;
for (i = 0; i < 5; i++){
printf("%i\n", arr[i]);
}
return 0;
}

C++语言的处理方式
函数指针方式
#include 
#include 
using namespace std;
inline bool Sort(int a,int b){
return a > b;
}
inline void Display(int a){
cout << a << endl;
}
int main() {
int arr[5] = { 4, 1, 2, 5, 6 };
sort(arr, arr+5,Sort);
for_each(arr,arr+5,Display);
return 0;
}
函数模板方式
#include 
#include 
using namespace std;
template
inline bool Sort(T const& a,T const& b){
return a > b;
}
template
inline void Display(T a){
cout << a << endl;
}
int main() {
int arr[5] = { 4, 1, 2, 5, 6 };
sort(arr, arr+5,Sort);
for_each(arr,arr+5,Display);
return 0;
}
仿函数方式
class Sort{
public:
bool operator()(int a,int b) const {
return a > b;
}
};
class Display{
public:
void operator()(int a) const{
cout << a << endl;
}
};
int main()
{
int arr[5] = { 4, 1, 2, 5, 6 };
sort(arr, arr+5,Sort());
for_each(arr,arr+5,Display());
return 0;
}
仿函数模板方式
template
class Sort{
public:
inline bool operator()(T const& a,T const& b) const {
return a > b;
}
};
template
class Display{
public:
inline void operator()(T const& a) const{
cout << a << endl;
}
};
int main()
{
int arr[5] = { 4, 1, 2, 5, 6 };
sort(arr, arr+5,Sort());
for_each(arr,arr+5,Display());
return 0;
}

在头文件中定义了如下三类仿函数:
1. 算术类仿函数
操作	仿函数
加	plus
减	minus
乘	multiplies
除	divides
取模	modulus
取反	negate
2. 关系运算类仿函数
操作	仿函数
等于	equal_to
不等于	not_equal_to
大于	greater
大于等于	greater_equal
小于	less
小于等于	less_equal
3. 逻辑运算仿函数
操作	仿函数
逻辑与	logical_and
逻辑或	logical_or
逻辑否	logical_not

*/

/*
第六章 容器
容器用来管理某类对象。为了应付程序中的不同需求,STL 准备了两类共七种基本容器类型:
序列式容器(Sequence containers),此为可序群集,其中每个元素均有固定位置—取决于插入时机和地点,和元素值无关。
如果你以追加方式对一个群集插入六个元素,它们的排列次序将和插入次序一致。STL提供了三个序列式容器:向量(vector)、双端队列(deque)、列表(list),此外你也可以把 string 和 array 当做一种序列式容器。
关联式容器(Associative containers),此为已序群集,元素位置取决于特定的排序准则以及元素值,和插入次序无关。
如果你将六个元素置入这样的群集中,它们的位置取决于元素值,和插入次序无关。STL提供了四个关联式容器:集合(set)、多重集合(multiset)、映射(map)和多重映射(multimap)。

vector
vector(向量): 是一种序列式容器,事实上和数组差不多,但它比数组更优越。一般来说数组不能动态拓展,因此在程序运行的时候不是浪费内存,就是造成越界。
而 vector 正好弥补了这个缺陷,当内存空间不够时,需要重新申请一块足够大的内存并进行内存的拷贝。
特点
拥有一段连续的内存空间,因此它能非常好的支持随机访问,即 [] 操作符和 .at(),随机访问快。(优点)
当向其头部或中间插入或删除元素时,为了保持原本的相对次序,插入或删除点之后的所有元素都必须移动,所以插入或删除的效率比较低。(缺点)
在后面插入删除元素最快,此时一般不需要移动内存。(优点)
总结:相当于可拓展的数组(动态数组),随机访问快,在头部和中间插入或删除效率低,但在尾部插入或删除效率高。

适用场景
适用于对象简单,变化较小,并且频繁随机访问的场景。

例子
以下例子针对整型定义了一个 vector,插入 6 个元素,然后打印所有元素:
*/

void vector_use()
{
	cout << "vector_use start";
	vector vecTemp;

	for (int i = 0; i<6; i++)
		vecTemp.push_back(i);

	for (int i = 0; i
void deque_use()
{
	cout << "deque_use start";
	deque dequeTemp;

	for (int i = 0; i<6; i++)
		dequeTemp.push_back(i);

	for (int i = 0; i
void list_use()
{
	cout << "list_use start";
	list listTemp;

	for (char c = 'a'; c <= 'z'; ++c)
		listTemp.push_back(c);

	while (!listTemp.empty())
	{
		cout << listTemp.front() << " ";
		listTemp.pop_front();
	}

	cout << "list_use end";
}
/*
成员函数empty()的返回值告诉我们容器中是否还有元素,只要这个函数返回 false,循环就继续进行。循环之内,成员函数front()会返回第一个元素,pop_front()函数会删除第一个元素。
注意:list<指针> 完全是性能最低的做法,还不如直接使用 list<对象> 或使用 vector<指针> 好,因为指针没有构造与析构,也不占用很大内存。

set
set(集合)顾名思义,就是数学上的集合——每个元素最多只出现一次,并且 set 中的元素已经从小到大排好序。

特点
使用红黑树实现,其内部元素依据其值自动排序,每个元素值只能出现一次,不允许重复。
每次插入值的时候,都需要调整红黑树,效率有一定影响。(缺点)
map 和 set 的插入或删除效率比用其他序列容器高,因为对于关联容器来说,不需要做内存拷贝和内存移动。(优点)
总结:由红黑树实现,其内部元素依据其值自动排序,每个元素值只能出现一次,不允许重复,且插入和删除效率比用其他序列容器高。

适用场景
适用于经常查找一个元素是否在某集合中且需要排序的场景。

例子
下面的例子演示 set(集合)的两个特点:
*/
#include 
void set_use()
{
	cout << "set_use start";
	set setTemp;

	setTemp.insert(3);
	setTemp.insert(1);
	setTemp.insert(2);
	setTemp.insert(1);

	set::iterator it;
	for (it = setTemp.begin(); it != setTemp.end(); it++)
	{
		cout << *it << " ";
	}

	cout << "set_use end";
}
/*
输出结果:1 2 3。一共插入了 4 个数,但是集合中只有 3 个数并且是有序的,可见之前说过的 set 集合的两个特点,有序和不重复。

当 set 集合中的元素为结构体时,该结构体必须实现运算符 ‘<’ 的重载:
*/
struct People
{
	string name;
	int age;

	bool operator <(const People p) const
	{
		return age < p.age;
	}
};

void set_use1()
{
	cout << "set_use1 start";
	set setTemp;

	setTemp.insert({ "张三", 14 });
	setTemp.insert({ "李四", 16 });
	setTemp.insert({ "隔壁老王", 10 });

	set::iterator it;
	for (it = setTemp.begin(); it != setTemp.end(); it++)
	{
		printf("姓名:%s 年龄:%d\n", (*it).name.c_str(), (*it).age);
	}
	/*
	输出结果
	姓名:王二麻子 年龄:10
	姓名:张三 年龄:14
	姓名:李四 年龄:16
	*/
	cout << "set_use1 end";
}
/*
可以看到结果是按照年龄由小到大的顺序排列。另外 string 要使用c_str()转换一下,否则打印出的是乱码。
另外 Multiset 和 set 相同,只不过它允许重复元素,也就是说 multiset 可包括多个数值相同的元素。这里不再做过多介绍。

map
map 由红黑树实现,其元素都是 “键值/实值” 所形成的一个对组(key/value pairs)。
map 主要用于资料一对一映射的情况,map 内部自建一颗红黑树,这颗树具有对数据自动排序的功能,所以在 map 内部所有的数据都是有序的。
比如一个班级中,每个学生的学号跟他的姓名就存在着一对一映射的关系。

特点
每个元素都有一个键,且只能出现一次,不允许重复。
根据 key 值快速查找记录,查找的复杂度基本是 O(logN),如果有 1000 个记录,二分查找最多查找 10次(1024)。(优点)
每次插入值的时候,都需要调整红黑树,效率有一定影响。(缺点)
增加和删除节点对迭代器的影响很小,除了那个操作节点,对其他的节点都没有什么影响。(优点)
对于迭代器来说,可以修改实值,而不能修改 key。
总结:元素为键值对,key 和 value 可以是任意你需要的类型,每个元素都有一个键,且只能出现一次,不允许重复,根据 key 快速查找记录。

适用场景
适用于需要存储一个数据字典,并要求方便地根据key找value的场景。

例子
*/
#include 
void map_use1()
{
	cout << "map_use1 start";
	map mapTemp;

	mapTemp.insert({ 5, "张三" });
	mapTemp.insert({ 3, "李四" });
	mapTemp.insert({ 4, "隔壁老王" });

	map::iterator it;
	for (it = mapTemp.begin(); it != mapTemp.end(); it++)
	{
		printf("学号:%d 姓名:%s\n", (*it).first, (*it).second.c_str());
	}
	/*
	输出结果:
	学号:3 姓名:李四
	学号:4 姓名:隔壁老王
	学号:5 姓名:张三
	*/
	cout << "map_use1 end";
}
/*
multimap 和 map 相同,但允许重复元素,也就是说 multimap 可包含多个键值(key)相同的元素。这里不再做过多介绍。

容器配接器
除了以上七个基本容器类别,为满足特殊需求,STL还提供了一些特别的(并且预先定义好的)容器配接器,根据基本容器类别实现而成。包括:
1、stack
名字说明了一切,stack 容器对元素采取 LIFO(后进先出)的管理策略。
2、queue
queue 容器对元素采取 FIFO(先进先出)的管理策略。也就是说,它是个普通的缓冲区(buffer)。
3、priority_queue
priority_queue 容器中的元素可以拥有不同的优先权。所谓优先权,乃是基于程序员提供的排序准则(缺省使用 operators)而定义。
Priority queue 的效果相当于这样一个 buffer:“下一元素永远是queue中优先级最高的元素”。如果同时有多个元素具备最髙优先权,则其次序无明确定义。

各容器的特点总结

vector 支持随机访问,在头部和中间插入或删除效率低,但在尾部插入或删除效率高。
支持随机访问,但效率没有 vector 高,在头部和尾部插入或删除效率高,但在中间插入或删除效率低。
list 不支持随机访问,在任意位置的插入和删除效率都较高。
set 由红黑树实现,其内部元素依据其值自动排序,每个元素值只能出现一次,不允许重复,且插入和删除效率比用其他序列容器高。
map 的元素为键值对,key 和 value 可以是任意你需要的类型,每个元素都有一个键,且只能出现一次,不允许重复,根据 key 快速查找记录。
在实际使用过程中,到底选择这几种容器中的哪一个,应该根据遵循以下原则:

1、如果需要高效的随机访问,不在乎插入和删除的效率,使用 vector。
2、如果需要随机访问,并且关心两端数据的插入和删除效率,使用 deque。
3、如果需要大量的插入和删除元素,不关心随机访问的效率,使用 list。
4、如果经常查找一个元素是否在某集合中且需要排序,唯一存在的情况使用 set,不唯一存在的情况使用 multiset。
5、如果打算存储数据字典,并且要求方便地根据 key 找到 value,一对一的情况使用 map,一对多的情况使用 multimap。

各容器的时间复杂度分析

vector 在头部和中间位置插入和删除的时间复杂度为 O(N),在尾部插入和删除的时间复杂度为 O(1),查找的时间复杂度为 O(1);
deque 在中间位置插入和删除的时间复杂度为 O(N),在头部和尾部插入和删除的时间复杂度为 O(1),查找的时间复杂度为 O(1);
list 在任意位置插入和删除的时间复杂度都为 O(1),查找的时间复杂度为 O(N);
set 和 map 都是通过红黑树实现,因此插入、删除和查找操作的时间复杂度都是 O(log N)。

各容器的共性

各容器一般来说都有下列函数:默认构造函数、复制构造函数、析构函数、empty()、max_size()、size()、operator=、operator<、operator<=、operator>、operator>=、operator==、operator!=、swap()。

顺序容器和关联容器都共有下列函数:
begin() :返回容器第一个元素的迭代器指针;
end():返回容器最后一个元素后面一位的迭代器指针;
rbegin():返回一个逆向迭代器指针,指向容器最后一个元素;
rend():返回一个逆向迭代器指针,指向容器首个元素前面一位;
clear():删除容器中的所有的元素;
erase(it):删除迭代器指针it处元素。
*/



int _tmain(int argc, _TCHAR* argv[])
{
	iterator_use1();
	system("pause");
	return 0;
}

你可能感兴趣的:(c++,开发语言)