#
下面舉例說明如何定義和使用函數對象。首先,聲明一個普通的類并重載“()”操作符: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