四、学习要领
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;
}