C++泛型編程綜合講解
函數(shù)模板
進(jìn)一步把函數(shù)中的或類中的數(shù)據(jù)類型進(jìn)一步抽象,這個(gè)抽象的類型就叫泛型
模板:函數(shù)模板,類模板
模板就是把函數(shù)(或類)中的類型抽象出來,有指定類型方可使用
模板可以有默認(rèn)類型,類模板規(guī)則(函數(shù)模板,不存在規(guī)則):從右到左
模板編譯機(jī)制:
- 編譯器并不是把函數(shù)(類)模板處理成能夠處理任何類型的函數(shù)(類),而是一個(gè)函數(shù)(類)的生成器。
- 函數(shù)(類)模板通過具體類型產(chǎn)生不同的函數(shù)實(shí)例(類實(shí)體)。
- 編譯器會(huì)對(duì)函數(shù)(類)模板進(jìn)行兩次編譯,第一次在聲明的地方對(duì)模板本身進(jìn)行編譯(主要是對(duì)語法進(jìn)行檢查,在調(diào)用的地方對(duì)參數(shù)替換),再次對(duì)代碼進(jìn)行編譯,二次編譯也被稱之為延時(shí)編譯。
注意:模板函數(shù)需要編譯兩次,是慢于定義函數(shù)的原因
關(guān)鍵字template是模板標(biāo)識(shí)符
<>是泛型,指定的參數(shù)類型列表
class用來修飾泛型,typename
也可進(jìn)行修飾
#include <iostream> using namespace std; //int add(int a,int b){ // return a+b; //} //float add(float a,float b){ // return a+b; //} //string add(string a,string b){ // return a+b; //} //抽象的泛型 template<class T> T add(T a,T b){ cout << "i am is template" << endl; return a+b; } int main() { int a=10,b=20; cout << add(a,b) << endl; float a1=5.21; float b1=13.14; cout << add(a1,b1) << endl; string a2="yao",b2="liang"; cout << add(a2,b2) << endl;//隱式調(diào)用 cout << add<string>(a2,b2) << endl;//顯示調(diào)用 return 0; }
顯示調(diào)用和隱式調(diào)用
#include <iostream> using namespace std; //抽象的泛型 typename<class T> T add(T a,T b){ return a+b; } int main() { string a2="yao",b2="liang"; cout << add(a2,b2) << endl;//隱式調(diào)用 cout << add<string>(a2,b2) << endl;//顯示調(diào)用 return 0; }
函數(shù)模板的特化
前提:模板的特化(泛型沒有制定類型)是依賴基礎(chǔ)模板的
產(chǎn)生原因:當(dāng)函數(shù)的算法邏輯與實(shí)際的參數(shù)類型不匹配時(shí),就應(yīng)該對(duì)類型進(jìn)行特化
#include <iostream> using namespace std; template <class T> T compair(T t1,T t2){ return t1>t2?t1:t2; } //對(duì)基礎(chǔ)模板進(jìn)行全特化(函數(shù)模板只能全特化,不能偏特化) template <> const char* compair(const char* str1,const char* str2){ return string(str1)>string(str2)?str1:str2; } int main() { int a=10,b=20; cout << compair(a,b) << endl; const char* str1="yaoliang"; const char* str2="yao"; cout << compair(str1,str2) << endl; return 0; }
類型可以傳*號(hào)
#include <iostream> using namespace std; template <class T> T compair(T t1,T t2){//char *t1=name; cout << string(t1) << endl; } int main() { char *name="minmin"; char *name1="sun"; compair(name,name1); // int a=10; // int *p=&a; // int *q=&a; // compair(p,q); return 0; }
函數(shù)模板的調(diào)用優(yōu)先級(jí)
函數(shù)實(shí)例>匹配的特化模板>基礎(chǔ)模板
#include <iostream> using namespace std; template <class T> T compair(T t1,T t2){ cout << "i am is basics" <<endl; return t1>t2?t1:t2; } //對(duì)基礎(chǔ)模板進(jìn)行全特化(函數(shù)模板只能全特化,不能偏特化) template <> const char* compair(const char* str1,const char* str2){ cout << "i am is specialization" <<endl; return string(str1)>string(str2)?str1:str2; } inline int compair(int a,int b){ cout << "i am is inline fun" << endl; return a>b?a:b; } int main() { int a=10,b=20; cout << compair(a,b) << endl; const char* str1="yaoliang"; const char* str2="yao"; //如果是隱式調(diào)用,優(yōu)先調(diào)用與之類型相匹配的特化模板 cout << compair(str1,str2) << endl; //顯性調(diào)用,直接調(diào)用 cout << compair<const char*>(str1,str2) << endl; cout << compair<int>(a,b) << endl; return 0; }
函數(shù)模板的實(shí)參推演
函數(shù)模板具有函數(shù)特性:函數(shù)重載
#include <iostream> using namespace std; template <class T> T add(T t1,T t2){ cout << "i am is one_basics" <<endl; return t1+t2; } template <class T1,class T2> T1 add(T1 t1,T2 t2){ cout << "i am is two_basics" <<endl; return t1+t2; } int main() { int a=10,b=20; cout << add(a,b) << endl; double c=13.14; cout << add(c,a) << endl; return 0; }
函數(shù)泛型不僅是一個(gè)單一抽象類型,也可以是一個(gè)組合類型。
#include <iostream> #include <typeinfo>//信息識(shí)別頭 using namespace std; template <class T> void my_funtion(T t){ cout << "i am is basics" << endl; cout << typeid (t).name() << endl; } template <> void my_funtion(int* t){ cout << "指針類型的特化" << endl; cout << typeid (t).name() << endl; } template <class Ret,class Arg1,class Arg2> void my_funtion(Ret (*arg)(Arg1,Arg2)){ cout << typeid (Ret).name() << endl; cout << typeid (Arg1).name() << endl; cout << typeid (Arg2).name() << endl; cout << "指針類型的復(fù)合模板" << endl; } int add(int a,int b){ return a+b; } int main() { int a=10; my_funtion(a); int *p=&a; my_funtion(p); my_funtion(add); return 0; }
在c++11關(guān)于函數(shù)模板的可變參符號(hào)…
…如果修飾類型(變量),則表示類型(變量)不定引數(shù),個(gè)數(shù)不同,類型不同的多個(gè)參數(shù)。
#include <iostream> using namespace std; //函數(shù)實(shí)例 void print(){ }; template <class FirstArg,class... Args> void print(FirstArg firstArg,Args... args){//int firstArg=100,...(3.14,"yaoliang") //3.14 ...("yaoliang") //"yaoliang" ...() cout << firstArg << " "; print(args...); } int main() { print(100,3.14,"yaoliang"); return 0; }
類模板
像聲明一個(gè)類一樣聲明一個(gè)模板,無隱式調(diào)用,模板規(guī)則:使用默認(rèn)泛型參數(shù)類型,從右向左依次指定
#include <iostream> using namespace std; template <class T1,class T2> class Person{ private: T1 _name; T2 _age; public: Person(T1 name,T2 age){ this->_age=age; this->_name=name; } int getAge(){ return this->_age; } string getName(){ return this->_name; } virtual void showInfo(){ cout << "姓名:" << this->_name << ",年齡:" << this->_age << endl; } }; template <class T1,class T2,class T3=int> class Stu:public Person<T1,T2> { private: const T3 _id; static int count; public: Stu(T1 name,T2 age,T3 id):Person<T1,T2>(name,age),_id(id){ count++; } void showInfo()override{ cout << "學(xué)號(hào):" << this->_id << ",姓名:" << this->getName() << ",年齡:" << this->getAge() << endl; } static int get_count(){ return count; } }; template <class T1,class T2,class T3> int Stu<T1,T2,T3>::count=0; int main() { Person<string,int> *person=new Stu<string,int,int>("yao",19,1); person->showInfo(); delete person; Stu<string,int> stu("sunsun",18,2);//使用缺省類型,從右往左 stu.showInfo(); cout << Stu<string,int,int>::get_count() << endl; return 0; }
分文件編程實(shí)現(xiàn)一個(gè)順序棧
注意: .hpp
是類模板文件,聲明和定義在同一個(gè)文件中
stack_cpp.hpp
:
#ifndef MY_STACK_HPP #define MY_STACK_HPP #include <exception> #include <stdexcept> #include <iostream> using namespace std; template <class T> class my_stack{ private: T* m_data; int capacity; int size; public: my_stack(int c=10); ~my_stack(); bool full(); bool empty(); void push(const T& val); void pop(); T& top(); }; #endif // MY_STACK_HPP template<class T> my_stack<T>::my_stack(int c) { this->capacity=c; this->m_data=new T[capacity]; this->size=0; } template<class T> my_stack<T>::~my_stack() { if(nullptr!=this->m_data){ delete [] this->m_data; this->m_data=nullptr; } capacity=size=0; } template<class T> bool my_stack<T>::full() { return size==capacity; } template<class T> bool my_stack<T>::empty() { return size==0; } template<class T> void my_stack<T>::push(const T &val) { if(full()){ return; } m_data[size]=val; size++; } template<class T> void my_stack<T>::pop() { if(this->empty()){ throw range_error("空了"); } size--; } template<class T> T &my_stack<T>::top() { return m_data[size-1]; }
main.cpp
:
#include <iostream> #include "my_stack.hpp" using namespace std; int main() { my_stack<int> s; s.push(1); s.push(2); s.push(3); while (!s.empty()) { cout << s.top() << endl; s.pop(); } return 0; }
內(nèi)嵌類
為外圍類服務(wù),不影響外圍類
#include <iostream> #include <vector> using namespace std; template <class T> class A{ public: int a; class B{ public: int b=10; }; }; int main() { cout << sizeof (A<int>) << endl;//4 A<int>::B b_obj; cout << b_obj.b << endl; cout << "------------vetor容器---------------" << endl; vector<int> v; for(int i=0;i<10;i++){ v.push_back(rand()%100+1); } vector<int>::iterator it; for(it=v.begin();it!=v.end();it++){ cout << *it << " "; } cout << endl; return 0; }
注意: 外圍類和內(nèi)圍類之間不能相互訪問,特殊的:靜態(tài)屬性
類模板的特化
#include <iostream> using namespace std; template <class T> class A{ public: A(){ cout << " A basics" << endl; } }; template <> class A<int> { public: A(){ cout << " A 全特化 " << endl; } }; template <class T> class A<T*> { public: A(){ cout << " A 偏特化" << endl; } }; template <> class A<int*> { public: A(){ cout << " A 的全特化" << endl; } }; template <class Ret,class Arg1,class Arg2> class A<Ret (*)(Arg1,Arg2)>{ public: A(){ cout << " A 的偏特化" << endl; } }; int add(int a,int b){ return a+b; } int main() { A<int> a; A<float> a1; A<int *> a2; A<int(*)(int,int)> a3=add; return 0; }
類實(shí)例>匹配的全特化模板>匹配的偏特化模板>基礎(chǔ)模板
函數(shù)符(Function)
函數(shù)對(duì)象(Functor),仿函數(shù)
保存函數(shù)調(diào)用簽名的形式:
- 全局函數(shù)指針
- 成員指針
- 函數(shù)對(duì)象
- lambda表達(dá)式
函數(shù)對(duì)象:是類對(duì)象,這個(gè)類對(duì)象的類中有一個(gè)小括號(hào)重載運(yùn)算符函數(shù)。
#include <iostream> using namespace std; template<class T> class A{ private: T str; public: inline A(const T& t){ this->str=t; } inline void operator()(){ cout << this->str << endl; } }; void showInfo(){ cout << "hello" << endl; } int main() { showInfo(); cout << "---------------------------------" << endl; A<string> a("functor is hello"); a(); return 0; }
特點(diǎn):
函數(shù)對(duì)象是類對(duì)象,當(dāng)類對(duì)象調(diào)用成員函數(shù)時(shí),函數(shù)符合內(nèi)聯(lián)條件,自動(dòng)升級(jí)為內(nèi)聯(lián)函數(shù),調(diào)用比普通函數(shù)效率高
函數(shù)對(duì)象可以直接使用類中定義的屬性
函數(shù)對(duì)象具有具體的類型
函數(shù)對(duì)象一般不會(huì)單獨(dú)使用,一般作為算法策略使用:
#include <iostream> using namespace std; template <class T> T my_greate(T t1,T t2){ return t1>t2?t1:t2; } template <class T> T my_less(T t1,T t2){ return t1<t2?t1:t2; } template <class T,class Compair>//Compair是獲取到的函數(shù)類型 T是獲取到的數(shù)據(jù)類型 T compair(T t1,T t2,Compair f){//Compair f=my_greate<int> return f(t1,t2); } //聲明兩個(gè)函數(shù)對(duì)象 template <class T> class my_Greate{ public: T operator()(T t1,T t2){ return t1>t2?t1:t2; } }; template <class T> class my_Less{ public: T operator()(T t1,T t2){ return t1<t2?t1:t2; } }; int main() { int a=10,b=20; cout << "獲取較大的值" << compair(a,b,my_greate<int>) << endl; cout << "獲取較小的值" << compair(a,b,my_less<int>) << endl; cout << "使用函數(shù)對(duì)象,提高調(diào)用效率" << endl; cout << "獲取較大的值" << compair(a,b,my_Greate<int>()) << endl; cout << "獲取較小的值" << compair(a,b,my_Less<int>()) << endl; return 0; }
函數(shù)對(duì)象術(shù)語
當(dāng)函數(shù)對(duì)象的類中的小闊號(hào)運(yùn)算符只有一個(gè)形參,所定義對(duì)象時(shí),這個(gè)對(duì)象叫做一元函數(shù)對(duì)象
當(dāng)函數(shù)對(duì)象的類中的小闊號(hào)運(yùn)算符只有二個(gè)形參,所定義對(duì)象時(shí),這個(gè)對(duì)象叫做二元函數(shù)對(duì)象
當(dāng)函數(shù)對(duì)象的類中的小闊號(hào)運(yùn)算符有多個(gè)形參,所定義對(duì)象時(shí),這個(gè)對(duì)象叫做多元函數(shù)對(duì)象
當(dāng)函數(shù)對(duì)象的類中的小闊號(hào)運(yùn)算符返回值時(shí)一個(gè)bool
類型,這個(gè)對(duì)象叫做謂詞(Predicate)
匿名函數(shù)對(duì)象Lambda表達(dá)式
Lambda表達(dá)式分析:
- []是函數(shù)對(duì)象的構(gòu)造函數(shù)中的形參,獲取外部實(shí)參時(shí)傳遞的形式
- []為空時(shí),代表無參的空構(gòu)造,對(duì)于lambda不進(jìn)行捕獲
- [=]相當(dāng)于函數(shù)對(duì)象中的類中的構(gòu)造函數(shù)為拷貝傳參(值的傳遞)
- [&]相當(dāng)于函數(shù)對(duì)象中的類中的構(gòu)造函數(shù)為引用傳遞(別名)
- ()相當(dāng)于小闊號(hào)運(yùn)算符的形參列表
- {}相當(dāng)于括號(hào)運(yùn)算符的函數(shù)體
- 在lambda的形參列表后使用->返回值類型,明確返回值的類型
#include <iostream> using namespace std; class Lambda{ private: // int _a; int& _b; public: // Lambda(){ // } // Lambda(int& a){ // //相當(dāng)于構(gòu)造函數(shù)中是一個(gè)值的拷貝 // this->_a=a; // } Lambda(int& b):_b(b){ //相當(dāng)于構(gòu)造函數(shù)中是一個(gè)值的拷貝 this->_b=b; } void operator()(){ cout << "hello world!" << endl; } }; int main() { //c++11 auto關(guān)鍵字:表示由編譯器自動(dòng)推導(dǎo)出的數(shù)據(jù)類型。不可作為函數(shù)形參 auto f=[](){cout << "hello world" << endl;}; f(); // Lambda()(); // auto f1=Lambda(); // f1(); // int a=100; // auto f2=[=](){ // cout << a << endl; // }; // f2(); int b=10; cout << "b的地址:" << &b << endl; auto f3=[&](){ cout << "b的地址:" << &b << endl; }; f3(); int x=100,y=200; auto f4=[&]()mutable{//mutable易變關(guān)鍵字,與const關(guān)鍵字相反 int temp=x; x=y; y=temp; }; f4(); cout << "x=" << x << " y=" << y << endl; return 0; }
包裝器
類模板std::function 是通用的多態(tài)函數(shù)封裝器。 std::function 的實(shí)例能存儲(chǔ)、復(fù)制及調(diào)用任何可調(diào)用對(duì)象。C++語言中有多種可調(diào)用對(duì)象:函數(shù)、函數(shù)指針、lambda表達(dá)式、bind創(chuàng)建的對(duì)象以及重載了函數(shù)調(diào)用運(yùn)算符的類(仿函數(shù))等。
和其他對(duì)象一樣,可調(diào)用對(duì)象也有類型。如:每個(gè)lambda有它自己唯一的(未命名)類類型;函數(shù)及函數(shù)指針的類型則由其返回值類型和實(shí)參類型決定。然而,不同類型的可調(diào)用對(duì)象可能共享同一種調(diào)用形式。調(diào)用形式指明了返回的類型以及傳遞給調(diào)用的實(shí)參類型。一種調(diào)用形式對(duì)應(yīng)一個(gè)函數(shù)(function)類型。
標(biāo)準(zhǔn)使用:
#include <iostream> #include <functional> using namespace std; int add(int a,int b){//add函數(shù)類型:int (int ,int ) return a+b; } class A{ public: int add(int a,int b){//int A::(A* const,int,int) return a+b; } }; class B{ public: int operator()(int a,int b){//int A::(int,int) return a+b; } }; int main() { //使用標(biāo)準(zhǔn)包裝器function包裝全局函數(shù) function<int (int,int)> f1=add; cout << f1(10,20) << endl; //使用標(biāo)準(zhǔn)包裝器function包裝類成員函數(shù) A a; function<int(A* const,int,int)> f2=&A::add; cout << f2(&a,20,30) << endl; //使用標(biāo)準(zhǔn)包裝器function包裝一個(gè)函數(shù)對(duì)象 function<int(int,int)> f3=B(); cout << f3(10,20) <<endl; //使用標(biāo)準(zhǔn)包裝器function包裝一個(gè)Lambda表達(dá)式 function <int (int,int)> f4=[](int a,int b){return a+b;}; cout << f4(100,220) << endl; return 0; }
封裝一個(gè)包裝器:
#include <iostream> #include <functional> using namespace std; template <class T> class My_function{ public: My_function(){ cout << "my_function is basics" << endl; } }; //模板偏特化 template<class Ret,class Arg1,class Arg2> class My_function<Ret (Arg1,Arg2)> { private: //typedef Ret(*Pfunc)(Arg1,Arg2); using Pfunc=Ret (*)(Arg1,Arg2); Pfunc f; public: My_function(Pfunc f){ this->f=f; } //包裝器核心 Ret operator()(Arg1 arg1,Arg2 arg2){ return f(arg1,arg2); } }; int add(int a,int b){//類型 int (int ,int) return a+b; } int main() { My_function<int (int,int)> f1=add; cout << f1(10,20) << endl; std::function<int(int,int)> f2=add; cout << f2(20,40) << endl; return 0; }
到此這篇關(guān)于C++泛型編程綜合講解的文章就介紹到這了,更多相關(guān)C++泛型編程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vscode 配置 C/C++ 編譯環(huán)境的詳細(xì)圖文教程
這篇文章主要介紹了vscode 配置 C/C++ 編譯環(huán)境的詳細(xì)教程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05Linux系統(tǒng)中C語言編程創(chuàng)建函數(shù)fork()執(zhí)行解析
最近在看進(jìn)程間的通信,看到了fork()函數(shù),雖然以前用過,這次經(jīng)過思考加深了理解?,F(xiàn)總結(jié)如下2013-04-04C++實(shí)現(xiàn)LeetCode(158.用Read4來讀取N個(gè)字符之二 - 多次調(diào)用)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(158.用Read4來讀取N個(gè)字符之二 - 多次調(diào)用),本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07OpenGL實(shí)現(xiàn)不規(guī)則區(qū)域填充算法
這篇文章主要為大家詳細(xì)介紹了OpenGL實(shí)現(xiàn)不規(guī)則區(qū)域填充算法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-02-02c++如何將一個(gè)char轉(zhuǎn)化為string
這篇文章主要介紹了c++如何將一個(gè)char轉(zhuǎn)化為string問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08C++11中std::move、std::forward、左右值引用、移動(dòng)構(gòu)造函數(shù)的測(cè)試問題
這篇文章主要介紹了C++11中std::move、std::forward、左右值引用、移動(dòng)構(gòu)造函數(shù)的測(cè)試,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09