#
下面举例说明如何定义和使用函数对象。首先,声明一个普通的类并重载“()”操作符:class Negate {public: int operator() (int n) { return -n;} }; 重载操作语句中,记住第一个圆括弧总是空的,因为它代表重载的操作符名;第二个圆括弧是参数列表。一般在重载操作符时,参数数量是固定的,而重载“()” 操作符时有所不同,它可以有任意多个参数。 因为在Negate中内建的操作是一元的(只有一个操作数),重载的“()”操作符也只有一个参数。返回类型与参数类型相同-本例中为int。函数返回与参数符号相反的整数。使用函数对象 我们现在定义一个叫Callback()的函数来测试函数对象。Callback()有两个参数:一个为int一个是对类Negate的引用。 Callback()将函数对象neg作为一个普通的函数名:#include <iostream>using std::cout;void Callback(int n, Negate & neg) {int val = neg(n); //调用重载的操作符“()” cout << val;}不要的代码中,注意neg是对象,而不是函数。编译器将语句int val = neg(n);转化为int val = neg.operator()(n); 通常,函数对象不定义构造函数和析构函数。因此,在创建和销毁过程中就不会发生任何问题。前面曾提到过,编译器能内联重载的操作符代码,所以就避免了与函数调用相关的运行时问题。为了完成上面个例子,我们用主函数main()实现Callback()的参数传递:int main() {Callback(5, Negate() ); //输出 -5}本例传递整数5和一个临时Negate对象到Callback(),然后程序输出-5。模板函数对象 从上面的例子中可以看出,其数据类型被限制在int,而通用性是函数对象的优势之一,如何创建具有通用性的函数对象呢?方法是使用模板,也就是将重载的操作符“()”定义为类成员模板,以便函数对象适用于任何数据类型:如double,_int64或char:class GenericNegate{public: template <class T> T operator() (T t) const {return -t;}};int main(){GenericNegate negate;cout<< negate(5.3333); // doublecout<< negate(10000000000i64); // __int64}如果用普通的回调函数实现上述的灵活性是相当困难的。标准库中函数对象 C++标准库定义了几个有用的函数对象,它们可以被放到STL算法中。例如,sort()算法以判断对象(predicate object)作为其第三个参数。判断对象是一个返回Boolean型结果的模板化的函数对象。可以向sort()传递greater<>或者less<>来强行实现排序的升序或降序:#include <functional> // for greater<> and less<>#include <algorithm> //for sort() #include <vector>using namespace std;int main(){ vector <int> vi;//..填充向量sort(vi.begin(), vi.end(), greater<int>() );//降序( descending )sort(vi.begin(), vi.end(), less<int>() ); //升序 ( ascending )}
用C++的stl库,相信大家都有用vector的经历,毕竟vector支持直接下标方式取数据的确方便很多。
但是vector默认是不提供find方法的,所以我们在查找的时候,通常这样写代码:
vector<int> vec;for(unsigned int i = 0;i<vec.size();++i){ if(vec[i]==xxx) { break; }}
并不是说提供不了,而是stl库中实际上已经有通用的find函数(不止find……)
可以看一下下面的代码:
int main(int argc,char* argv[]){ vector<int> vec; vec.push_back(123); vec.push_back(456); vector<int>::iterator findit = find(vec.begin(),vec.end(),123); //vector<int>::iterator findit = find(vec.begin(),vec.end(),111); if(findit==vec.end()) { printf("no find\n"); } else { printf("find[%d]\n",*findit); } return 0;}
这样的写法会不会简单很多呢?需要说明的是,虽然这个通用的find方法也是可以用在map,set等上面的,但是效率会比容器内部的find方法慢很多,所以,除非容器实在是没有提供find方法,否则还是建议不要用公共的这一种。
另外,作为题外话,我们需要注意一下vector的删除(erase)操作。由于vector需要能以下标方式取数据,所以必须时刻保证连续的存储空间,对应于实现上,即,当删除vector中间的一个成员时,这个成员后面的所有成员,会以原顺序向前全部拷贝过来。有兴趣的朋友,可以用这个例子测试一下。这里起码告诉了我们两件事:
1.vector中一个成员被删除,会导致后面的成员进行copy和析构操作。2.vector不适合做有大量插入删除操作的容器,因为拷贝内存本身浪费很大
OK,到此为止啦~
这是一个用于比较的类模板,里面可以有多种用于比较的函数, 以IsEqual为例。
一、特化为绝对类型
也就是说直接为某个特定类型做特化,这是我们最常见的一种特化方式, 如特化为float, double等
// specialize for floattemplate<>class Compare<float>{public:static bool IsEqual(const float& lh, const float& rh){return abs(lh - rh) < 10e-3;}};
// specialize for doubletemplate<>class Compare<double>{public:static bool IsEqual(const double& lh, const double& rh){return abs(lh - rh) < 10e-6;}};
二、特化为引用,指针类型
这种特化我最初是在stl源码的的iterator_traits特化中发现的, 如下:
template <class _Iterator>struct iterator_traits {typedef typename _Iterator::iterator_category iterator_category;typedef typename _Iterator::value_type value_type;typedef typename _Iterator::difference_type difference_type;typedef typename _Iterator::pointer pointer;typedef typename _Iterator::reference reference;};
// specialize for _Tp*template <class _Tp>struct iterator_traits<_Tp*> {typedef random_access_iterator_tag iterator_category;typedef _Tp value_type;typedef ptrdiff_t difference_type;typedef _Tp* pointer;typedef _Tp& reference;};
// specialize for const _Tp*template <class _Tp>struct iterator_traits<const _Tp*> {typedef random_access_iterator_tag iterator_category;typedef _Tp value_type;typedef ptrdiff_t difference_type;typedef const _Tp* pointer;typedef const _Tp& reference;};
这种特化其实是就不是一种绝对的特化, 它只是对类型做了某些限定,但仍然保留了其一定的模板性,这种特化给我们提供了极大的方便, 如这里, 我们就不需要对int*, float*, double*等等类型分别做特化了。
三、特化为另外一个类模板
这其实是第二种方式的扩展,其实也是对类型做了某种限定,而不是绝对化为某个具体类型,如下:
这就把IsEqual的参数限定为一种vector类型, 但具体是vector<int>还是vector<float>, 我们可以不关心, 因为对于这两种类型,我们的处理方式是一样的,我们可以把这种方式称为“半特化”。
当然, 我们可以将其“半特化”为任何我们自定义的模板类类型:
这就是三种类型的模板特化, 我们可以这么使用这个Compare类:
// intint i1 = 10;int i2 = 10;bool r1 = Compare<int>::IsEqual(i1, i2);
// floatfloat f1 = 10;float f2 = 10;bool r2 = Compare<float>::IsEqual(f1, f2);
// doubledouble d1 = 10;double d2 = 10;bool r3 = Compare<double>::IsEqual(d1, d2);
// pointerint* p1 = &i1;int* p2 = &i2;bool r4 = Compare<int*>::IsEqual(p1, p2);
// vector<T>vector<int> v1;v1.push_back(1);v1.push_back(2);
vector<int> v2;v2.push_back(1);v2.push_back(2);bool r5 = Compare<vector<int> >::IsEqual(v1, v2);
// custom template classSpecializedType<float> s1 = {10.1f,10.2f};SpecializedType<float> s2 = {10.3f,10.0f};bool r6 = Compare<SpecializedType<float> >::IsEqual(s1, s2);
模板有两种特化,全特化和偏特化(局部特化)
模板函数只能全特化,没有偏特化(以后可能有)。
模板类是可以全特化和偏特化的。
全特化,就是模板中模板参数全被指定为确定的类型。
全特化也就是定义了一个全新的类型,全特化的类中的函数可以与模板类不一样。
偏特化,就是模板中的模板参数没有被全部确定,需要编译器在编译时进行确定。
在类型上加上const、&、*( cosnt int、int&、int*、等等)并没有产生新的类型。只是类型被修饰了。模板在编译时,可以得到这些修饰信息。
模板的特化是非常有用的。它像一个在编译期的条件判断。当编译器在编译时找到了符合的特化实现,就会使用这个特化实现。这就叫编译器多态(或者叫静态多态)。这种东西对编写基础库是很有用的。这也就是为何c++的基础库大量使用了模板技术,而且大量使用了特化,特别是偏特化。
在泛型中,利用特化类得到类新的特性,以便找到最适合这种特性的实现。而这一切都是在编译时完成。
Memset 用来对一段内存空间全部设置为某个字符,一般用在对定义的字符串进行初始化为‘ ’或‘\0’;
主要应用是初始化某个内存空间例:char a[100];memset(a, '\0', sizeof(a));
memset可以方便的清空一个结构类型的变量或数组。
如:
struct sample_struct{ char csName[16]; int iSeq; int iType;};
对于变量struct sample_strcut stTest;
一般情况下,清空stTest的方法:
stTest.csName[0]='\0';stTest.iSeq=0;stTest.iType=0;
用memset就非常方便:memset(&stTest,0,sizeof(struct sample_struct));
如果是数组:
struct sample_struct TEST[10];则memset(TEST,0,sizeof(struct sample_struct)*10);
memcpy 用来做内存拷贝,你可以拿它拷贝任何数据类型的对象,可以指定拷贝的数据长度。
memcpy是用于copy源空间的数据到目的空间中例:char a[100],b[50]; memcpy(b, a, sizeof(b));注意如用sizeof(a),会造成b的内存地址溢出。
Strcpy 就只能拷贝字符串了,它遇到'\0'就结束拷贝。
例:char a[100],b[50];strcpy(a,b);如用strcpy(b,a),要注意a中的字符串长度(第一个‘\0’之前)是否超过50位,如超过,则会造成b的内存地址溢出。
strcpy用于字符串copy,遇到‘\0’,将结束
Copyright @ IT菜鸟 Powered by: .Text and ASP.NET Theme by: .NET Monster