四、学习要领
 1)如果容器有成员函数,则使用成员函数,如果没有才考虑用 STL 的算法函数。
 2)把全部的 STL 算法函数过一遍,知道大概有些什么东西。
 3)如果打算采用某算法函数,一定要搞清楚它的原理,关注它的效率。
 4)不要太看重这些算法函数,自己写一个也就那么回事。
 5)不是因为简单,而是因为不常用。 五、常用函数
 1)for_each()遍历
 2)find()遍历
 3)find_if()遍历
 4)find_not_if()遍历
 5)sort()排序
 STL 的 sort 算法,数据量大时采用 QuickSort(快速排序),分段归并排序。一旦分段后的数据量小于
 某个门槛(16),为避免 QuickSort 的递归调用带来过大的额外负荷,就改用 InsertSort(插入排序)。
 如果递归层次过深,还会改用 HeapSort(堆排序)。
 适用于数组容器 vector、string、deque(list 容器有 sort 成员函数,红黑树和哈希表没有排序的说
 法)。
 6)二分查找
 示例(foreach):
 #include <iostream>
 #include <vector>
 #include <list>
 #include <algorithm>
 using namespace std;
 template<typename T>
 void zsshow(const T& no) // 张三的个性化表白函数。
 {
 cout << "亲爱的" << no << "号:我是一只傻傻鸟。\n";
 }
 template<typename T>
 class czs // 张三的个性化表白仿函数。
 {
 public:
 void operator()(const T& no) {
 cout << "亲爱的" << no << "号:我是一只傻傻鸟。\n";
 }
 };
 template<typename T1, typename T2>
 void foreach(const T1 first, const T1 last, T2 pfun)
 {
 for (auto it = first; it != last; it++)
 pfun(*it); // 以超女编号为实参调用类的 operator()函数。
 }
 int main()
 {
 vector<int> bh = { 5,8,2,6,9,3,1,7 }; // 存放超女编号的容器。
 //list<string> bh = { "05","08","02","06","09","03","01","07" }; // 存放超女编号的容器。
 // 写一个函数,在函数中遍历容器,向超女表白,表白的方法可自定义。
 foreach(bh.begin(), bh.end(), zsshow<int>); // 第三个参数是模板函数。
 foreach(bh.begin(), bh.end(), czs<int>()); // 第三个参数是仿函数。
 }
 示例(findif):
 #include <iostream>
 #include <vector>
 #include <list>
 #include <algorithm>
 using namespace std;
 template<typename T>
 bool zsshow(const T& no,const T & in_no) // 张三的个性化表白函数。
 {
 if (no != in_no) return false;
 cout << "亲爱的" << no << "号:我是一只傻傻鸟。\n";
 return true;
 }
 template<typename T>
 class czs // 张三的个性化表白仿函数。
 {
 public:
 bool operator()(const T& no, const T& in_no) {
 if (no != in_no) return false;
 cout << "亲爱的" << no << "号:我是一只傻傻鸟。\n";
 return true;
 }
 };
 template<typename T1, typename T2, typename T3>
 T1 findif(const T1 first, const T1 last, T2 pfun,T3 in_no)
 {
 for (auto it = first; it != last; it++)
 if (pfun(*it, in_no) ==true) return it; // 用迭代器调用函数对象。
 return last;
 }
 int main()
 {
 vector<int> bh = { 5,8,2,6,9,33,1,7 }; // 存放超女编号的容器。
 //list<string> bh = { "05","08","02","06","09","03","01","07" }; // 存放超女编号的容器。
 auto it1=findif(bh.begin(), bh.end(), zsshow<int>,2); // 第三个参数是模板函数。
 if (it1 == bh.end()) cout << "查找失败。\n";
 else cout << "查找成功:" << *it1 << endl;
 auto it2=findif(bh.begin(), bh.end(), czs<int>(),33); // 第三个参数是仿函数。
 if (it2 == bh.end()) cout << "查找失败。\n";
 else cout << "查找成功:" << *it2 << endl;
 }
 示例(findif 仿函数):
 #include <iostream>
 #include <vector>
 #include <list>
 #include <algorithm> // STL 算法函数头文件。
 using namespace std;
 template<typename T>
 bool zsshow(const T& no) // 张三的个性化表白函数。
 {
 if (no != 3) return false;
 cout << "亲爱的" << no << "号:我是一只傻傻鸟。\n";
 return true;
 }
 template<typename T>
 class czs // 张三的个性化表白仿函数。
 {
 public:
 T m_no; // 存放张三喜欢的超女编号。
 czs(const T& no) : m_no(no) {} // 构造函数的参数是张三喜欢的超女编号。
 bool operator()(const T& no) {
 if (no != m_no) return false;
 cout << "亲爱的" << no << "号:我是一只傻傻鸟。\n";
 return true;
 }
 };
 template<typename T1, typename T2>
 T1 findif(const T1 first, const T1 last, T2 pfun)
 {
 for (auto it = first; it != last; it++)
 if (pfun(*it) ==true) return it; // 用迭代器调用函数对象。
 return last;
 }
 int main()
 {
 vector<int> bh = { 5,8,2,6,9,33,1,7 }; // 存放超女编号的容器。
 //list<string> bh = { "05","08","02","06","09","03","01","07" }; // 存放超女编号的容器。
 auto it1=find_if(bh.begin(), bh.end(), zsshow<int>); // 第三个参数是模板函数。
 if (it1 == bh.end()) cout << "查找失败。\n";
 else cout << "查找成功:" << *it1 << endl;
 auto it2=find_if(bh.begin(), bh.end(), czs<int>(8)); // 第三个参数是仿函数。
 if (it2 == bh.end()) cout << "查找失败。\n";
 else cout << "查找成功:" << *it2 << endl;
 }
 示例(bsort):
 #include <iostream>
 #include <vector>
 #include <list>
 #include <algorithm> // STL 算法函数。
 #include <functional> // STL 仿函数。
 using namespace std;
 template<typename T>
 bool compasc(const T& left, const T& right) { // 普通函数,用于升序。
 return left < right;
 }
 template<typename T>
 struct _less
 {
 bool operator()(const T& left, const T& right) { // 仿函数,用于升序。
 return left < right;
 }
 };
 template<typename T>
 bool compdesc(const T& left, const T& right) { // 普通函数,用于降序。
 return left > right;
 }
 template<typename T>
 class _greater
 {
 public:
 bool operator()(const T& left, const T& right) { // 仿函数,用于降序。
 return left > right;
 }
 };
 template<typename T, typename compare>
 void bsort(const T first, const T last, compare comp) // 冒泡排序。
 {
 while(true)
 {
 bool bswap = false; // 本轮遍历已交换过元素的标识,true-交换过,false-未交
 换过。
 for (auto it = first; ; )
 {
 auto left = it; // 左边的元素。
 it++;
 auto right = it; // 右边的元素。
 if (right == last) break; // 表示 it1 已经是最后一个元素了。
 //if (*left > *right) // 如果左边的元素比右边大,交换它们的值。
 //if (*left < *right) // 如果左边的元素比右边小,交换它们的值。
 // 排序规则:如果 comp()返回 true,left 排在前面(升序),否则 right 排在前面(降
 序)。
 if (comp(*left, *right) == true) continue;
 // 交换两个元素的值。
 auto tmp = *right; *right = *left; *left = tmp;
 bswap = true; // 一轮遍历已交换过元素的标识。
 }
 if (bswap == false) break; // 如果在 for 循环中不曾交换过元素,说明全部的元素已有序。
 }
 }
 int main()
 {
 vector<int> bh = { 5,8,2,6,9,33,1,7 }; // 存放超女编号的容器。
 //list<string> bh = { "05","08","02","06","09","03","01","07" }; // 存放超女编号的容器。
 //bsort(bh.begin(), bh.end(),compasc<int>); // 普通函数(升序)。
 //bsort(bh.begin(), bh.end(), compdesc<int>); // 普通函数(降序)。
 //bsort(bh.begin(), bh.end(),_less<int>()); // 仿函数(升序)。
 //bsort(bh.begin(), bh.end(), _greater<int>()); // 仿函数(降序)。
 //bsort(bh.begin(), bh.end(), less<int>()); // STL 提供的仿函数(升序)。
 //bsort(bh.begin(), bh.end(), greater<int>()); // STL 提供的仿函数(降序)。
 //sort(bh.begin(), bh.end(),_less<int>()); // 仿函数(升序)。
 sort(bh.begin(), bh.end(), _greater<int>()); // 仿函数(降序)。
 for (auto val : bh)
 cout << val << " ";
 cout << endl;
 }
 示例(for_each):
 #include <iostream>
 #include <vector>
 #include <list>
 #include <algorithm>
 using namespace std;
 template<typename T>
 struct girl {
 T m_yz; // 统计的颜值。
 int m_count; // 符合条件的元素个数。
 girl(const T yz) : m_yz(yz), m_count(0) {}
 void operator()(const T& yz) {
 if (yz==m_yz) m_count++;
 }
 };
 int main()
 {
 vector<int> vv = { 1,3,2,4,1,2,3,1,4,3 }; // 1-极漂亮;2-漂亮;3-普通;4-歪瓜裂枣
 girl<int> g=for_each(vv.begin(), vv.end(), girl<int>(1)); // 按颜值统计超女人数。
 cout << "g.m_count=" << g.m_count << endl;
 }
 193、智能指针 unique_ptr
 unique_ptr 独享它指向的对象,也就是说,同时只有一个 unique_ptr 指向同一个对象,当这个
 unique_ptr 被销毁时,指向的对象也随即被销毁。
 包含头文件:#include <memory>
 template <typename T, typename D = default_delete<T>>
 class unique_ptr
 {
 public:
 explicit unique_ptr(pointer p) noexcept; // 不可用于转换函数。
 ~unique_ptr() noexcept;
 T& operator*() const; // 重载*操作符。
 T* operator->() const noexcept; // 重载->操作符。
 unique_ptr(const unique_ptr &) = delete; // 禁用拷贝构造函数。
 unique_ptr& operator=(const unique_ptr &) = delete; // 禁用赋值函数。
 unique_ptr(unique_ptr &&) noexcept; // 右值引用。
 unique_ptr& operator=(unique_ptr &&) noexcept; // 右值引用。
 // ... private:
 pointer ptr; // 内置的指针。
 };
 第一个模板参数 T:指针指向的数据类型。
 第二个模板参数 D:指定删除器,缺省用 delete 释放资源。
 测试类 AA 的定义:
 class AA
 {
 public:
 string m_name;
 AA() { cout << m_name << "调用构造函数 AA()。\n"; }
 AA(const string & name) : m_name(name) { cout << "调用构造函数 AA("<< m_name <<
 ")。\n"; }~AA() { cout << m_name << "调用了析构函数~AA(" << m_name << ")。\n"; }
 }; 一、基本用法
 1)初始化
 方法一:
 unique_ptr<AA> p0(new AA("西施")); // 分配内存并初始化。
 方法二:
 unique_ptr<AA> p0 = make_unique<AA>("西施"); // C++14 标准。
 unique_ptr<int> pp1=make_unique<int>(); // 数据类型为 int。
 unique_ptr<AA> pp2 = make_unique<AA>(); // 数据类型为 AA,默认构造函数。
 unique_ptr<AA> pp3 = make_unique<AA>("西施"); // 数据类型为 AA,一个参数的构造函数。
 unique_ptr<AA> pp4 = make_unique<AA>("西施",8); // 数据类型为 AA,两个参数的构造函数。
 方法三(不推荐):
 AA* p = new AA("西施");
 unique_ptr<AA> p0(p); // 用已存在的地址初始化。
 2)使用方法
  智能指针重载了*和->操作符,可以像使用指针一样使用 unique_ptr。
  不支持普通的拷贝和赋值。
 AA* p = new AA("西施");
 unique_ptr<AA> pu2 = p; // 错误,不能把普通指针直接赋给智能指针。
 unique_ptr<AA> pu3 = new AA("西施"); // 错误,不能把普通指针直接赋给智能指针。
 unique_ptr<AA> pu2 = pu1; // 错误,不能用其它 unique_ptr 拷贝构造。
 unique_ptr<AA> pu3;
 pu3 = pu1; // 错误,不能用=对 unique_ptr 进行赋值。
  不要用同一个裸指针初始化多个 unique_ptr 对象。
  get()方法返回裸指针。
  不要用 unique_ptr 管理不是 new 分配的内存。
 3)用于函数的参数
  传引用(不能传值,因为 unique_ptr 没有拷贝构造函数)。
  裸指针。
 4)不支持指针的运算(+、-、++、--)
 二、更多技巧
 1)将一个 unique_ptr 赋给另一个时,如果源 unique_ptr 是一个临时右值,编译器允许这样做;如
 果源 unique_ptr 将存在一段时间,编译器禁止这样做。一般用于函数的返回值。
 unique_ptr<AA> p0;
 p0 = unique_ptr<AA>(new AA ("西瓜"));
 2)用 nullptr 给 unique_ptr 赋值将释放对象,空的 unique_ptr==nullptr。
 3)release()释放对原始指针的控制权,将 unique_ptr 置为空,返回裸指针。(可用于把 unique_ptr
 传递给子函数,子函数将负责释放对象)
 4)std::move()可以转移对原始指针的控制权。(可用于把 unique_ptr 传递给子函数,子函数形参
 也是 unique_ptr)
 5)reset()释放对象。
 void reset(T * _ptr= (T *) nullptr);
 pp.reset(); // 释放 pp 对象指向的资源对象。
 pp.reset(nullptr); // 释放 pp 对象指向的资源对象
 pp.reset(new AA("bbb")); // 释放 pp 指向的资源对象,同时指向新的对象。
 6)swap()交换两个 unique_ptr 的控制权。
 void swap(unique_ptr<T> &_Right);
 7)unique_ptr 也可象普通指针那样,当指向一个类继承体系的基类对象时,也具有多态性质,如同
 使用裸指针管理基类对象和派生类对象那样。
 8)unique_ptr 不是绝对安全,如果程序中调用 exit()退出,全局的 unique_ptr 可以自动释放,但
 局部的 unique_ptr 无法释放。
 9)unique_ptr 提供了支持数组的具体化版本。
 数组版本的 unique_ptr,重载了操作符[],操作符[]返回的是引用,可以作为左值使用。
 // unique_ptr<int[]> parr1(new int[3]); // 不指定初始值。
 unique_ptr<int[]> parr1(new int[3]{ 33,22,11 }); // 指定初始值。
 cout << "parr1[0]=" << parr1[0] << endl;
 cout << "parr1[1]=" << parr1[1] << endl;
 cout << "parr1[2]=" << parr1[2] << endl;
 unique_ptr<AA[]> parr2(new AA[3]{string("西施"), string("冰冰"), string("幂幂")});
 cout << "parr2[0].m_name=" << parr2[0].m_name << endl;
 cout << "parr2[1].m_name=" << parr2[1].m_name << endl;
 cout << "parr2[2].m_name=" << parr2[2].m_name << endl;
 示例 1:
 #include <iostream>
 #include <memory>
 using namespace std;
 class AA
 {
 public:
 string m_name;
 AA() { cout << m_name << "调用构造函数 AA()。\n"; }
 AA(const string & name) : m_name(name) { cout << "调用构造函数 AA("<< m_name <<
 ")。\n"; }~AA() { cout << "调用了析构函数~AA(" << m_name << ")。\n"; }
 };
 // 函数 func1()需要一个指针,但不对这个指针负责。
 void func1(const AA* a) {
 cout << a->m_name << endl;
 }
 // 函数 func2()需要一个指针,并且会对这个指针负责。
 void func2(AA* a) {
 cout << a->m_name << endl;
 delete a;
 }
 // 函数 func3()需要一个 unique_ptr,不会对这个 unique_ptr 负责。
 void func3(const unique_ptr<AA> &a) {
 cout << a->m_name << endl;
 }
 // 函数 func4()需要一个 unique_ptr,并且会对这个 unique_ptr 负责。
 void func4(unique_ptr<AA> a) {
 cout << a->m_name << endl;
 }
 int main()
 {
 unique_ptr<AA> pu(new AA("西施"));
 cout << "开始调用函数。\n";
 //func1(pu.get()); // 函数 func1()需要一个指针,但不对这个指针负责。
 //func2(pu.release()); // 函数 func2()需要一个指针,并且会对这个指针负责。
 //func3(pu); // 函数 func3()需要一个 unique_ptr,不会对这个 unique_ptr
 负责。
 func4(move(pu)); // 函数 func4()需要一个 unique_ptr,并且会对这个 unique_ptr 负责。
 cout << "调用函数完成。\n";
 if (pu == nullptr) cout << "pu 是空指针。\n";
 }
 示例 2:
 #include <iostream>
 #include <memory>
 using namespace std;
 class AA
 {
 public:
 string m_name;
 AA() { cout << m_name << "调用构造函数 AA()。\n"; }
 AA(const string & name) : m_name(name) { cout << "调用构造函数 AA("<< m_name <<
 ")。\n"; }~AA() { cout << "调用了析构函数~AA(" << m_name << ")。\n"; }
 };
 int main()
 {
 //AA* parr1 = new AA[2]; // 普通指针数组。
 AA* parr1 = new AA[2]{ string("西施"), string("冰冰") };
 //parr1[0].m_name = "西施 1";
 //cout << "parr1[0].m_name=" << parr1[0].m_name << endl;
 //parr1[1].m_name = "西施 2";
 //cout << "parr1[1].m_name=" << parr1[1].m_name << endl;
 //delete [] parr1;
 unique_ptr<AA[]> parr2(new AA[2]); // unique_ptr 数组。
 //unique_ptr<AA[]> parr2(new AA[2]{ string("西施"), string("冰冰") });
 parr2[0].m_name = "西施 1";
 cout << "parr2[0].m_name=" << parr2[0].m_name << endl;
 parr2[1].m_name = "西施 2";
 cout << "parr2[1].m_name=" << parr2[1].m_name << endl;
 }