《C++ STL开发技术引导》 第五章-C++ STL泛化技术分析笔记

19 篇文章 0 订阅

for_each的尝试

template<class InputIter, class T1, class T2>
T2 for_each(InputIter first, InputIter last, T1 f) {
	for (; first != last; ++first)
        f(*first);
    return f;
} 

在上面的for_each算法代码中,模版类型T1是函数f的一个类型,但在C++语言中,变量有类型,函数并没有类型,因此不能找到一个具体的C++类型来具现模板类型T1,这样繁华的算法实现是行不通的。
由此,自然会提出函数对象的编程模式,用一个结构体来封装函数,内部提供operator()操作符函数,支持传统函数形式的调用。如此,既解决了函数类型的问题,又可以继续使用f()形式的函数调用。与函数指针相比较,使用函数对象调用函数的速度更快,并且可拥有多个函数对象的实例。函数对象的一般定义如下,关键在内部要定义一个"()"操作符。

struct f {
	void operator()(T x) {......}			// 定义operator()函数,使f(T x)形式有意义
};

如下是SGI C++ STL提供的for_each算法代码。模板类型T1用Function代替,表明是一个函数对象,删去模板类型T2,返回函数对象。

template <class InputIter, class Function>
Function for_each(InputIter first, InputIter last, Function f) {
    for (; first != last; ++first)
        f(*first);
    return f;
}

for_each的实例

#include <iostream>
#include <algorithm>

using namespace std;

struct print {
    void operator()(int a, int b) { printf("%d\n", a + b)};	    // 定义operator()函数,使f(T x)形式有意义
};

int main()
{
	int a[] = {1,2,3,4,5,6};
	const int len = sizeof(a) / sizeof(int);
	for_each(a, a +len, print());
	return 0;
}

为了方面对容器的元素进行基本的算术逻辑运算,C++ STL定义了一些常用的函数对象,供算法调用。这些函数对象都是相应二元关系函数的一个封装,从结构体binary_function继承过来。

binary_function结构体的作用是,提供参数类型和返回值类型的信息,以便在必要时可将二元函数对象转换为一元函数对象。如下是SGI C++ STL在stl_function.h文件提供的函数对象。

template <class Arg1, class Arg2, class Result>
struct binary_function {
    typedef Arg1 first_argument_type;
    typedef Arg2 second_argument_type;
    typedef Result result_type;
};

template <class T>
struct plus : public binary_function<T, T, T> {
	T operator()(const T& x, const T& y) const { return x +y; }
};

template minus : public binary_function<T, T, T> {
	T operator()(const T& x, const T& y) const { return x - y; }
};

适配器是一种泛化的模板类型。通过传递一个具现的C++类型给适配器的模板类型参数, 就可以生成一种新的类型,从而在已有类型的基础上完成新类型的构造。

适配器本身是一个新的自定义类型 class/struct,其中会包含一个或多个辅助的所适配的类型的成员,并对内含的成员的接口进行改造,再以新的适配器类型向外部提供接口。

适配器分为迭代器适配器、函数对象适配器和容器适配器。

一般而言,容器是以 class templates 完成,算法以 function templates 完成,仿函数是一种将 operator() 重载的 class template, 迭代器则是一种将 operator++ 和 operator* 等指针习惯常行为重载的 class template。

C++ STL的反向迭代器reverse_iterator提供字符串的尾端开始查找字符。reverse_iterator用类而不是结构体来实现,是由于内部需要用protected来控制变量的访问权限。

逆向迭代器并不改变存储中的实际位置,只是改变了逻辑位置。逆向迭代器 reverse_iterator 就是对 正向迭代器 iterator 的二次包装,在保持和利用正向迭代器的原有行为的同时,将一个正向迭代器转换为逆向迭代器,实现了逻辑上的相反遍历的功能。
在这里插入图片描述

template <class Iterator>
class reverse_iterator
{
protected:
	Iterator current;

public:     
    // 使用关键字typename说明其后标识符是一个类型,而不是类的变量,进而用typedef重定义位一个规范的类型名字
	typedef typename iterator_traits<Iterator>::iterator_category iterator_category;
	typedef typename iterator_traits<Iterator>::value_type value_type;
	typedef typename iterator_traits<Iterator>::diffetence_type difference_type;
	typedef typename iterator_traits<Iterator>::pointer pointer;
	typedef typename iterator_traits<Iterator>::reference reference;
	typedef Iterator iterator_type;
	typedef reverse_iterator<Iterator> Self;

public:
	reverse_iterator{}
	explicit reverse_iterator(iterator_type x) :current(x) {}
	reverse_iterator(const Self& x) : current(x.current) {}

	iterator_type base() const { return current; }

	reference operator*() const {
		Iterator tmp = current;
		return *tmp;
	}

	Self& operatpr++() {	// 实际执行"--"
		--current;
		return *this;
	}

	Self operator++(int) {
		Self tmp = *this;
		--current;
		return tmp;
	}

	Self& operatpr--() {	// 实际执行"++"
		++current;
		return *this;
	}

	Self operator--(int) {
		Self tmp = *this;
		++current;
		return tmp;
	}

	Self operator+(difference_type n) const {
		return Self(current - n);
	}

	Self operator+=(difference_type n) {
		current -= n;
		return *this;
	}

	Self operator-(difference_type n) const {
		return Self(current + n);
	}

	Self operator-=(difference_type n) {
		current += n;
		return *this;
	}

	reference operator[](difference_type n) const {
		return *(*this + n);
	}
};

reverse_iterator例子

#include <iterator>
#include <iostream>
#include <vector>

int main(void)
{
	using namespace std;
	vector<int> v;
	v.push_back(3);
    v.push_back(6);
	v.push_back(9);
	reverse_iterator<vector<int>::iterator, int> rfirst(v.end());
	reverse_iterator<vector<int>::iterator, int> rend(v.begin());
	while(rfirst != rend) {
  		cout << *rfirst << end;
  		++rfirst;
   }
   return 0;
}

下面再介绍一种迭代器适配器insert_iterator。与reverse_iterator不同,这个插入迭代器并不适用iterator来做模板类型,而是使用一个容器Container* container。insert_iterator可直接利用容器的插入函数,提供一种进行"="赋值插入方式。

对于 insert iterator,可以将一般迭代器的赋值(assign)操作,转化为插入(insert)操作。
insert_iterator的实现思路为:每个 insert_iterators 内部都维护一个由用户指定的容器和容器相应的迭代器;当客户端对 insert iterators 做赋值操作时,就在 insert_iterators 的 operator= 操作符中调用底层容器的 insert() 函数。对于 insert iterators 的前进、后退、取值、成员取用等操作都是关闭的,或是不允许的。
在这里插入图片描述

template <class Container>
class insert_iterator {
protected:
	Container* container;
	typename Container::iterator iter;
public:
	typedef Container container_type;		// 包含一个容器
	typedef output_iterator_tag iterator_category;
	typedef void value_type;
	typedef void difference_type;
	typedef void pointer;
	typedef void reference;

	insert_iterator(Container& x, typename Container::iterator i) : container(&x), iter(i) {}

	insert_iterator<Container&> operator=(const typename Container::value_type& value) {
		iter = container->insert(iter, value);		// 调用容器的insert()函数插入数据
		++iter;										// 继续移动迭代器
		return *this;
	}

	insert_iterator<Container>& operator*() { return *this; }
	insert_iterator<Container>& operator++() { return *this; }		//实际并没有移动
	insert_iterator<Container>& operator++(int) { return *this; }    //实际并没有移动

};

insert_iterator例子

List<int> L;
L.push_back(3);
insert_iterator<List<int>> ii(L, L.begin());
*ii++ = 0;
*ii++ = 1;
*ii++ = 2;

一些在算法中调用的函数对象,要求是一个返回bool值的一元函数对象,以使容器的每一个元素可以被这个一元函数对象进行判断处理。从下面的查找算法find_if的代码,可以看到代码中的函数对象pred,必须是一个返回bool值的单参数函数对象。

template<class InputIter, class Predicate>   // 返回bool值的函数对象,称为谓词判断Predicate
InputIter find_if(InputIter first, InputIter last, Predicate pred, input_iterator_tag)
{
	while(first != last && !pred(*first))	// 要求pred是返回bool值的一元函数(对象)
	    ++first;
	return first;
}

一个返回bool值的函数对象,称为谓词判断Predicate,如greater、less、not_equal_to等基本的函数对象。

函数对象适配器的作用在于,将双参数的函数对象转化为单参数的函数对象,以适应算法的调用要求。

传递某个值给二元函数对象,也就是所谓的绑定,C++ STL提供了两种用于绑定的函数对象适配器:binder1st/bind1st和binder2nd/bind2nd,分别将数值绑定二元函数的第一个参数和第二个参数。

binder1st/bind1st、binder2st/bind2st的实现:

//一元函数结构
template <class Arg, class Result>
struct unary_function 
{
    typedef Arg argument_type; //参数类型,可以理解为x对应的类型
    typedef Result result_type;//返回值类型,可以理解为 z 对应的类型
};

//二元函数结构
template <class Arg1, class Arg2, class Result>
struct binary_function
 {
    typedef Arg1 first_argument_type; //第一个参数类型,可以理解为x对应的类型
    typedef Arg2 second_argument_type;//第二个参数类型,可以理解为y对应的类型
    typedef Result result_type; //返回值类型,可以理解为 z 对应的类型
};

// unary_function 是一元函数结构。Operation是两个参数的函数对象,binder1st是一个单参数对象,类型是Operation的第二参数的类型。
template <class Operation>
class binder1st : public unary_function<typename Operation::second_argument_type, typename Operation::result_type>
{
protected:
	Operation op;									// 用于绑定二元函数对象,提供op()函数处理
	typename Operation::first_argument_type value;     // first_argument_type表示第一个参数类型,可以理解为x对应的类型

public:
	// y是指需要绑定的常数值
	binder1st(const Operation& x, const typename Operation::first_argument_type& y)
		: op(x), value(y) {}

	// operator()的返回值类型,调用op()
	typename Operation::result_type	operator()(const typename Operation::second_argument_type& x) const {
		return op(value, x);			// value绑定到Operation第一个参数,x用作第二个参数
	}

};

template<class Operation, class T>
inline binder1st<Operation> bind1st(const Operation& fn, const T& x)
{
	typedef typename Operation::first_argument_type Arg1_type;
	return binder1st<Operation>(fn, Arg1_type(x));
}

template <class Operation>
class binder2nd : public unary_function<typename Operation::first_argument_type, typename Operation::result_type>
{
protected:
	Operation op;									// 用于绑定二元函数对象,提供op()函数处理
	typename Operation::second_argument_type value;

public:
	// y是指需要绑定的常数值
	binder2nd(const Operation& x, const typename Operation::second_argument_type& y)
		: op(x), value(y) {}

	// operator()的返回值类型,调用op()
	typename Operation::result_type	operator()(const typename Operation::first_argument_type& x) const {
		return op(value, x);			// value绑定到Operation第一个参数,x用作第二个参数
	}

};

template<class Operation, class T>
inline binder2nd<Operation> bind2nd(const Operation& fn, const T& x)
{
	typedef typename Operation::second_argument_type Arg2_type;

	return binder2nd<Operation>(fn, Arg2_type(x));
}

参考文档:https://www.cnblogs.com/chuyongliu/p/5308616.html

实例如下

#include <iostream>
#include <algorithm>	// count_if
#include <functional>	// binder
#include <list>
 
using namespace std;
 
int main()
{
	// Data
	int iarray[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
	list<int> aList(iarray, iarray + 10);
 
	// binder和bind区别:
	// 1. 类绑定器有binder1st和binder2nd,而函数绑定器是bind1st和bind2nd
	// 2. bind是一个全局的模板函数其返回值为一个binder模板类的实例
	// 3. binder要指定泛型
	int k = count_if(aList.begin(), aList.end(), binder1st<greater<int>>(greater<int>(), 5));
 
	// bind1st bind2nd功能比较
	// k = count_if(aList.begin(), aList.end(), bind1st(greater<int>(), 5)); 
	// bind1st(greater<int>(), 5); //---->5 > x 即5作为第一个固定参数。返回5大于的数字的个数
	// bind2nd(greater<int>(), 5); //---->x > 5 即5作为第二个固定参。返回大于5的数字的个数
 
	cout << k << endl;
 
	system("pause");
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值