C++泛型編程綜合講解
函數(shù)模板
進(jìn)一步把函數(shù)中的或類中的數(shù)據(jù)類型進(jìn)一步抽象,這個抽象的類型就叫泛型
模板:函數(shù)模板,類模板
模板就是把函數(shù)(或類)中的類型抽象出來,有指定類型方可使用
模板可以有默認(rèn)類型,類模板規(guī)則(函數(shù)模板,不存在規(guī)則):從右到左
模板編譯機(jī)制:
- 編譯器并不是把函數(shù)(類)模板處理成能夠處理任何類型的函數(shù)(類),而是一個函數(shù)(類)的生成器。
- 函數(shù)(類)模板通過具體類型產(chǎn)生不同的函數(shù)實例(類實體)。
- 編譯器會對函數(shù)(類)模板進(jìn)行兩次編譯,第一次在聲明的地方對模板本身進(jìn)行編譯(主要是對語法進(jìn)行檢查,在調(diào)用的地方對參數(shù)替換),再次對代碼進(jìn)行編譯,二次編譯也被稱之為延時編譯。
注意:模板函數(shù)需要編譯兩次,是慢于定義函數(shù)的原因
關(guān)鍵字template是模板標(biāo)識符
<>是泛型,指定的參數(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ù)類型不匹配時,就應(yīng)該對類型進(jìn)行特化
#include <iostream>
using namespace std;
template <class T>
T compair(T t1,T t2){
return t1>t2?t1:t2;
}
//對基礎(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;
}
類型可以傳*號
#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)先級
函數(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;
}
//對基礎(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ù)重載
#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ù)泛型不僅是一個單一抽象類型,也可以是一個組合類型。
#include <iostream>
#include <typeinfo>//信息識別頭
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ù)模板的可變參符號…
…如果修飾類型(變量),則表示類型(變量)不定引數(shù),個數(shù)不同,類型不同的多個參數(shù)。
#include <iostream>
using namespace std;
//函數(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;
}
類模板
像聲明一個類一樣聲明一個模板,無隱式調(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é)號:" << 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;
}
分文件編程實現(xiàn)一個順序棧
注意: .hpp是類模板文件,聲明和定義在同一個文件中
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;
}
類實例>匹配的全特化模板>匹配的偏特化模板>基礎(chǔ)模板
函數(shù)符(Function)
函數(shù)對象(Functor),仿函數(shù)
保存函數(shù)調(diào)用簽名的形式:
- 全局函數(shù)指針
- 成員指針
- 函數(shù)對象
- lambda表達(dá)式
函數(shù)對象:是類對象,這個類對象的類中有一個小括號重載運算符函數(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;
}
特點:
函數(shù)對象是類對象,當(dāng)類對象調(diào)用成員函數(shù)時,函數(shù)符合內(nèi)聯(lián)條件,自動升級為內(nèi)聯(lián)函數(shù),調(diào)用比普通函數(shù)效率高
函數(shù)對象可以直接使用類中定義的屬性
函數(shù)對象具有具體的類型
函數(shù)對象一般不會單獨使用,一般作為算法策略使用:
#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);
}
//聲明兩個函數(shù)對象
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ù)對象,提高調(diào)用效率" << endl;
cout << "獲取較大的值" << compair(a,b,my_Greate<int>()) << endl;
cout << "獲取較小的值" << compair(a,b,my_Less<int>()) << endl;
return 0;
}函數(shù)對象術(shù)語
當(dāng)函數(shù)對象的類中的小闊號運算符只有一個形參,所定義對象時,這個對象叫做一元函數(shù)對象
當(dāng)函數(shù)對象的類中的小闊號運算符只有二個形參,所定義對象時,這個對象叫做二元函數(shù)對象
當(dāng)函數(shù)對象的類中的小闊號運算符有多個形參,所定義對象時,這個對象叫做多元函數(shù)對象
當(dāng)函數(shù)對象的類中的小闊號運算符返回值時一個bool類型,這個對象叫做謂詞(Predicate)
匿名函數(shù)對象Lambda表達(dá)式
Lambda表達(dá)式分析:
- []是函數(shù)對象的構(gòu)造函數(shù)中的形參,獲取外部實參時傳遞的形式
- []為空時,代表無參的空構(gòu)造,對于lambda不進(jìn)行捕獲
- [=]相當(dāng)于函數(shù)對象中的類中的構(gòu)造函數(shù)為拷貝傳參(值的傳遞)
- [&]相當(dāng)于函數(shù)對象中的類中的構(gòu)造函數(shù)為引用傳遞(別名)
- ()相當(dāng)于小闊號運算符的形參列表
- {}相當(dāng)于括號運算符的函數(shù)體
- 在lambda的形參列表后使用->返回值類型,明確返回值的類型
#include <iostream>
using namespace std;
class Lambda{
private:
// int _a;
int& _b;
public:
// Lambda(){
// }
// Lambda(int& a){
// //相當(dāng)于構(gòu)造函數(shù)中是一個值的拷貝
// this->_a=a;
// }
Lambda(int& b):_b(b){
//相當(dāng)于構(gòu)造函數(shù)中是一個值的拷貝
this->_b=b;
}
void operator()(){
cout << "hello world!" << endl;
}
};
int main()
{
//c++11 auto關(guān)鍵字:表示由編譯器自動推導(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 的實例能存儲、復(fù)制及調(diào)用任何可調(diào)用對象。C++語言中有多種可調(diào)用對象:函數(shù)、函數(shù)指針、lambda表達(dá)式、bind創(chuàng)建的對象以及重載了函數(shù)調(diào)用運算符的類(仿函數(shù))等。
和其他對象一樣,可調(diào)用對象也有類型。如:每個lambda有它自己唯一的(未命名)類類型;函數(shù)及函數(shù)指針的類型則由其返回值類型和實參類型決定。然而,不同類型的可調(diào)用對象可能共享同一種調(diào)用形式。調(diào)用形式指明了返回的類型以及傳遞給調(diào)用的實參類型。一種調(diào)用形式對應(yīng)一個函數(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包裝一個函數(shù)對象
function<int(int,int)> f3=B();
cout << f3(10,20) <<endl;
//使用標(biāo)準(zhǔn)包裝器function包裝一個Lambda表達(dá)式
function <int (int,int)> f4=[](int a,int b){return a+b;};
cout << f4(100,220) << endl;
return 0;
}封裝一個包裝器:
#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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
c++調(diào)用動態(tài)庫LNK2019和LNK1120無法解析的外部命令
本文主要介紹了c++調(diào)用動態(tài)庫LNK2019和LNK1120無法解析的外部命令, 出現(xiàn)這個錯誤一般都是函數(shù)只找到聲明但沒有實現(xiàn),或者是少了什么鏈接庫,下面就來解決一下2024-06-06
C/C++獲取當(dāng)前時間的方法總結(jié)(最全)
這篇文章主要為大家整理了C/C++中獲取當(dāng)前時間的最全方法,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)和借鑒價值,需要的可以了解一下2023-03-03
C語言使用setjmp和longjmp實現(xiàn)一個簡單的協(xié)程
這篇文章主要為大家介紹了C語言使用setjmp和longjmp實現(xiàn)一個簡單的協(xié)程過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
Recommended C Style and Coding Standards中文翻譯版
本文翻譯自Recommended C Style and Coding Standards(C語言編碼風(fēng)格和標(biāo)準(zhǔn)),需要的朋友可以參考下2014-04-04
剖析C++中的常量表達(dá)式與省略號的相關(guān)作用
這篇文章主要介紹了C++中的常量表達(dá)式與省略號的相關(guān)作用,以及表達(dá)式中的可變參數(shù)模板示例,需要的朋友可以參考下2016-01-01

