#
下面舉例說明如何定義和使用函數(shù)對象。首先,聲明一個(gè)普通的類并重載“()”操作符:class Negate {public: int operator() (int n) { return -n;} }; 重載操作語句中,記住第一個(gè)圓括弧總是空的,因?yàn)樗碇剌d的操作符名;第二個(gè)圓括弧是參數(shù)列表。一般在重載操作符時(shí),參數(shù)數(shù)量是固定的,而重載“()” 操作符時(shí)有所不同,它可以有任意多個(gè)參數(shù)。 因?yàn)樵贜egate中內(nèi)建的操作是一元的(只有一個(gè)操作數(shù)),重載的“()”操作符也只有一個(gè)參數(shù)。返回類型與參數(shù)類型相同-本例中為int。函數(shù)返回與參數(shù)符號相反的整數(shù)。使用函數(shù)對象 我們現(xiàn)在定義一個(gè)叫Callback()的函數(shù)來測試函數(shù)對象。Callback()有兩個(gè)參數(shù):一個(gè)為int一個(gè)是對類Negate的引用。 Callback()將函數(shù)對象neg作為一個(gè)普通的函數(shù)名:#include <iostream>using std::cout;void Callback(int n, Negate & neg) {int val = neg(n); //調(diào)用重載的操作符“()” cout << val;}不要的代碼中,注意neg是對象,而不是函數(shù)。編譯器將語句int val = neg(n);轉(zhuǎn)化為int val = neg.operator()(n); 通常,函數(shù)對象不定義構(gòu)造函數(shù)和析構(gòu)函數(shù)。因此,在創(chuàng)建和銷毀過程中就不會發(fā)生任何問題。前面曾提到過,編譯器能內(nèi)聯(lián)重載的操作符代碼,所以就避免了與函數(shù)調(diào)用相關(guān)的運(yùn)行時(shí)問題。為了完成上面?zhèn)€例子,我們用主函數(shù)main()實(shí)現(xiàn)Callback()的參數(shù)傳遞:int main() {Callback(5, Negate() ); //輸出 -5}本例傳遞整數(shù)5和一個(gè)臨時(shí)Negate對象到Callback(),然后程序輸出-5。模板函數(shù)對象 從上面的例子中可以看出,其數(shù)據(jù)類型被限制在int,而通用性是函數(shù)對象的優(yōu)勢之一,如何創(chuàng)建具有通用性的函數(shù)對象呢?方法是使用模板,也就是將重載的操作符“()”定義為類成員模板,以便函數(shù)對象適用于任何數(shù)據(jù)類型:如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}如果用普通的回調(diào)函數(shù)實(shí)現(xiàn)上述的靈活性是相當(dāng)困難的。標(biāo)準(zhǔn)庫中函數(shù)對象 C++標(biāo)準(zhǔn)庫定義了幾個(gè)有用的函數(shù)對象,它們可以被放到STL算法中。例如,sort()算法以判斷對象(predicate object)作為其第三個(gè)參數(shù)。判斷對象是一個(gè)返回Boolean型結(jié)果的模板化的函數(shù)對象。可以向sort()傳遞greater<>或者less<>來強(qiáng)行實(shí)現(xiàn)排序的升序或降序:#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的經(jīng)歷,畢竟vector支持直接下標(biāo)方式取數(shù)據(jù)的確方便很多。
但是vector默認(rèn)是不提供find方法的,所以我們在查找的時(shí)候,通常這樣寫代碼:
vector<int> vec;for(unsigned int i = 0;i<vec.size();++i){ if(vec[i]==xxx) { break; }}
并不是說提供不了,而是stl庫中實(shí)際上已經(jīng)有通用的find函數(shù)(不止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;}
這樣的寫法會不會簡單很多呢?需要說明的是,雖然這個(gè)通用的find方法也是可以用在map,set等上面的,但是效率會比容器內(nèi)部的find方法慢很多,所以,除非容器實(shí)在是沒有提供find方法,否則還是建議不要用公共的這一種。
另外,作為題外話,我們需要注意一下vector的刪除(erase)操作。由于vector需要能以下標(biāo)方式取數(shù)據(jù),所以必須時(shí)刻保證連續(xù)的存儲空間,對應(yīng)于實(shí)現(xiàn)上,即,當(dāng)刪除vector中間的一個(gè)成員時(shí),這個(gè)成員后面的所有成員,會以原順序向前全部拷貝過來。有興趣的朋友,可以用這個(gè)例子測試一下。這里起碼告訴了我們兩件事:
1.vector中一個(gè)成員被刪除,會導(dǎo)致后面的成員進(jìn)行copy和析構(gòu)操作。2.vector不適合做有大量插入刪除操作的容器,因?yàn)榭截悆?nèi)存本身浪費(fèi)很大
OK,到此為止啦~
這是一個(gè)用于比較的類模板,里面可以有多種用于比較的函數(shù), 以IsEqual為例。
一、特化為絕對類型
也就是說直接為某個(gè)特定類型做特化,這是我們最常見的一種特化方式, 如特化為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特化中發(fā)現(xiàn)的, 如下:
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;};
這種特化其實(shí)是就不是一種絕對的特化, 它只是對類型做了某些限定,但仍然保留了其一定的模板性,這種特化給我們提供了極大的方便, 如這里, 我們就不需要對int*, float*, double*等等類型分別做特化了。
三、特化為另外一個(gè)類模板
這其實(shí)是第二種方式的擴(kuò)展,其實(shí)也是對類型做了某種限定,而不是絕對化為某個(gè)具體類型,如下:
這就把IsEqual的參數(shù)限定為一種vector類型, 但具體是vector<int>還是vector<float>, 我們可以不關(guān)心, 因?yàn)閷τ谶@兩種類型,我們的處理方式是一樣的,我們可以把這種方式稱為“半特化”。
當(dāng)然, 我們可以將其“半特化”為任何我們自定義的模板類類型:
這就是三種類型的模板特化, 我們可以這么使用這個(gè)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);
模板有兩種特化,全特化和偏特化(局部特化)
模板函數(shù)只能全特化,沒有偏特化(以后可能有)。
模板類是可以全特化和偏特化的。
全特化,就是模板中模板參數(shù)全被指定為確定的類型。
全特化也就是定義了一個(gè)全新的類型,全特化的類中的函數(shù)可以與模板類不一樣。
偏特化,就是模板中的模板參數(shù)沒有被全部確定,需要編譯器在編譯時(shí)進(jìn)行確定。
在類型上加上const、&、*( cosnt int、int&、int*、等等)并沒有產(chǎn)生新的類型。只是類型被修飾了。模板在編譯時(shí),可以得到這些修飾信息。
模板的特化是非常有用的。它像一個(gè)在編譯期的條件判斷。當(dāng)編譯器在編譯時(shí)找到了符合的特化實(shí)現(xiàn),就會使用這個(gè)特化實(shí)現(xiàn)。這就叫編譯器多態(tài)(或者叫靜態(tài)多態(tài))。這種東西對編寫基礎(chǔ)庫是很有用的。這也就是為何c++的基礎(chǔ)庫大量使用了模板技術(shù),而且大量使用了特化,特別是偏特化。
在泛型中,利用特化類得到類新的特性,以便找到最適合這種特性的實(shí)現(xiàn)。而這一切都是在編譯時(shí)完成。
Memset 用來對一段內(nèi)存空間全部設(shè)置為某個(gè)字符,一般用在對定義的字符串進(jìn)行初始化為‘ ’或‘\0’;
主要應(yīng)用是初始化某個(gè)內(nèi)存空間例:char a[100];memset(a, '\0', sizeof(a));
memset可以方便的清空一個(gè)結(jié)構(gòu)類型的變量或數(shù)組。
如:
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));
如果是數(shù)組:
struct sample_struct TEST[10];則memset(TEST,0,sizeof(struct sample_struct)*10);
memcpy 用來做內(nèi)存拷貝,你可以拿它拷貝任何數(shù)據(jù)類型的對象,可以指定拷貝的數(shù)據(jù)長度。
memcpy是用于copy源空間的數(shù)據(jù)到目的空間中例:char a[100],b[50]; memcpy(b, a, sizeof(b));注意如用sizeof(a),會造成b的內(nèi)存地址溢出。
Strcpy 就只能拷貝字符串了,它遇到'\0'就結(jié)束拷貝。
例:char a[100],b[50];strcpy(a,b);如用strcpy(b,a),要注意a中的字符串長度(第一個(gè)‘\0’之前)是否超過50位,如超過,則會造成b的內(nèi)存地址溢出。
strcpy用于字符串copy,遇到‘\0’,將結(jié)束
Copyright @ IT菜鳥 Powered by: .Text and ASP.NET Theme by: .NET Monster