網站首頁 編程語言 正文
1.模板?
1.1何為模板
即模子,生成器。
1.2C++的模板的形式有兩種
函數模板與類模板。
1.3如何定義一個函數模板
就像定義函數一樣,定義一個函數模板,把函數中類型抽象出來,
同時告訴編譯器下面的函數是一個函數模子或函數生成器。
1.4語法形式
template + <class T1, class T2 = double, class T3 = int ....>
//模板頭中的類型參數列表也可以有默認值。
T add(T a, T b)
{
return a + b;
}
1.5模板的編譯機制
1.編譯器并不是把模板處理成能夠處理任何類型的函數,而是一個函數或類的生成器。
2.函數模板通過具體類型產生不同的函數(產生了模板函數)
3.編譯器會對函數模板進行兩次編譯,第一次在聲明的地方對模板本身進行編譯,在調用的地方對參數替換后的代碼進行編譯使用函數模板與真正的函數,誰的調用效率高呢?當然是真正的函數,因為函數模板還需要編譯器進行翻譯一遍才能調用。所以現開發中并不一定要把所有函數你都要定義成函數模板,所以類型替換之后的函數模板就成了一個函數實例了,這樣才能調用。
代碼實例:
#include <iostream>
using namespace std;
template <class T>
T my_add(T a, T b)
{
return a + b;
}
int main()
{
my_add<int>(10,20);
my_add<float>(3.14f,5.21f);
my_add<double>(5.21,3.14);
return 0;
}
2.函數模板
2.1調用方式
顯式調用:函數名后使用<>尖括號指定具體參數調用。
using namespace std;
template <class T>
T my_add(T a, T b)
{
return a + b;
}
int main()
{
my_add<int>(10,20);
my_add<float>(3.14f,5.21f);
my_add<double>(5.21,3.14);
return 0;
}
隱式調用:由編譯器自動根據參數推導參數類型再調用。
#include <iostream>
using namespace std;
template <class T>
T my_add(T a, T b)
{
return a + b;
}
int main()
{
my_add(10,20);
my_add(3.14f,5.21f);
my_add(5.21,3.14);
return 0;
}
2.2函數模板的特化與調用優先級
當只有基礎模板和特化模板時。
#include <iostream>
using namespace std;
template <class T>
T my_add(T a,T b)
{
cout<<"這是一個基礎模板"<<endl;
return a+b;
}
template <class T>
T my_add(int a,int b)
{
cout<<"這是一個特化模板"<<endl;
return a+b;
}
int main()
{
cout<<my_add(10,20)<<endl;
return 0;
}
結果圖:
當只有基礎模板和特化模板時,隱式調用基礎模板。
當有基礎模板和特化模板,還有實例時候。
#include <iostream>
using namespace std;
template <class T>
T my_add(T a,T b)
{
cout<<"這是一個基礎模板"<<endl;
return a+b;
}
template <class T>
T my_add(int a,int b)
{
cout<<"這是一個特化模板"<<endl;
return a+b;
}
int my_add(int a,int b)
{
cout<<"這是一個實例"<<endl;
return a+b;
}
int main()
{
cout<<my_add(10,20)<<endl;
return 0;
}
結果圖:
當有基礎模板和特化模板,還有實例時候,隱式調用用實例。
當有基礎模板和特化模板,還有實例時候,顯示調用
#include <iostream>
using namespace std;
template <class T>
T my_add(T a,T b)
{
cout<<"這是一個基礎模板"<<endl;
return a+b;
}
template <class T>
T my_add(int a,int b)
{
cout<<"這是一個特化模板"<<endl;
return a+b;
}
int my_add(int a,int b)
{
cout<<"這是一個實例"<<endl;
return a+b;
}
int main()
{
cout<<my_add<int>(10,20)<<endl;
return 0;
}
結果圖:
當有基礎模板和特化模板,還有實例時候,顯示調用用特化模板。
總結:
當有函數實例時:隱式調用將直接調用函數實例。
如果沒有函數實例時,隱式調用將直接調用函數模板的基礎模板。
如果使用顯示調用,當優先調用特化的與類型匹配的函數模板。
3.可變參函數模板
3.1概念
所謂的可變參模板是指類型參數為一一個可變是類型,這個類型使用class...來修飾。
3.2代碼實現(實現一個c中的printf的函數)
#include <iostream>
using namespace std;
void printf()
{
}
template <class Firstarg,class... Arg>
void printf(Firstarg firstarg, Arg... arg)
{
cout<<firstarg;
printf(arg...);
}
int main()
{
printf("lisi","cc");
return 0;
}
結果圖:
4.類模板
4.1類模板的定義形式
注意:在使用類模板時,不存在編譯推導類型,必須手動指定具體類型。
template <class T1, class T2, class T3 ...>
//class修飾符也可使用typename來修飾。
class + 類名
{
//類模板的模板體。
private:
//類模板中的屬性。
public:
//類中的方法
protected:
};
4.2代碼實例
#include <iostream>
using namespace std;
template <class T1,class T2>
class A
{
T1 name;
T2 age;
public:
A(T1 name,T2 age)
{
this->age=age;
this->name=name;
}
void show_info()
{
cout<<"name="<<name<<",age="<<age<<endl;
}
};
int main()
{
// A<string,int> a("lisi",20);
// a.show_info();
A<string,int>* a=new A<string,int>("lisi",20);
a->show_info();
return 0;
}
結果圖:
5.類模板中的特殊屬性的初始化方式及繼承與多態
5.1代碼實例
#include <iostream>
using namespace std;
template <class T1,class T2>
class A
{
T1 name;
T2 age;
public:
A(T1 name,T2 age)
{
this->name=name;
this->age=age;
}
virtual void show_info()
{
cout<<"name="<<this->name<<",age"<<age<<endl;
}
void set_name(T1 name)
{
this->name=name;
}
T1 get_name()
{
return this->name;
}
void set_age(T2 age)
{
this->age=age;
}
T2 get_age()
{
return this->age;
}
};
template <class T1,class T2,class T3>
class B:public A<T1,T2>
{
const int id;
static int count;
public:
B(T1 name,T2 age,T3 _id):id(_id),A<T1,T2>(name,age)
{
}
void show_info()
{
cout<<"id="<<this->id<<",name="<<this->get_name()<<",age"<<this->get_age()<<endl;
}
};
int main()
{
//1.棧上
B<string,int,int> b("lisi",20,1001);
b.show_info();
//2.堆上
B<string,int,int>* b1=new B<string,int,int>("zhangsan",29,1002);
b1->show_info();
//3.實現多態
A<string,int>* a=new B<string,int,int>("wangwu",50,1003);
a->show_info();
return 0;
}
結果圖:
5.2使用類模板去實現一個數據結構
實現一個順序棧模板
首先我們使用一下多文件編程,類似于c的那種,我們會發現問題如下:
main.cpp文件:
#include <iostream>
#include "socket.h"
using namespace std;
int main()
{
socket<int> s(2);
return 0;
}
stack.h文件:
#ifndef SOCKET_H
#define SOCKET_H
using namespace std;
#include <iostream>
template <class T>
class socket
{
T* m_date;
int len;
int max_size;
public:
//構造
socket(int _len);
//析構
~socket();
//入棧
void push(const socket& other);
//出棧
void out();
//獲取棧頂的值
T get_out();
//判斷是否為空
bool is_empty();
};
#endif // SOCKET_H
stack.cpp文件:
#include "socket.h"
template <class T>
socket<T>::socket(int _max_size)
{
this->m_date=new T[len];
this->len=0;
this->max_size=_max_size;
}
結果圖:
結果分析:如圖所以,結果告訴我們無法連接到構造函數,這是由于我們使用的是模板類,模板類需要被編譯兩次,如果我們像這樣把stack.cpp和stack.h分開寫的話,stack.cpp里面的模板只被編譯了一次,所以我們無法連接到構造函數。
使分文件編程的方式實現一個模板棧:
在C++分文件編程時,在業內常用的一種文件標準是后綴為.hpp的模板文件。
代碼實現:
stack.cpp文件:
#ifndef STACK_HPP
#define STACK_HPP
using namespace std;
#include <iostream>
template <class T>
class Stack
{
T* my_data;
int len;
int max_size;
public:
//構造函數
Stack(int _max_size);
//析構函數
~Stack();
//入棧
void push(const int& other);
//出棧
void out_data();
//獲取棧頂的值
T get_data();
//判斷是否為空
bool is_empty();
};
#endif // STACK_HPP
template <class T>
Stack<T>::Stack(int _max_size)
{
this->my_data=new int[_max_size];
this->len=0;
this->max_size=_max_size;
}
template <class T>
Stack<T>::~Stack()
{
if(this->my_data!=nullptr){
delete this->my_data;
}
}
template <class T>
void Stack<T>::push(const int& other)
{
if(this->max_size<len){
return;
}
my_data[len]=other;
++(this->len);
}
template <class T>
void Stack<T>::out_data()
{
if(len<=0){
return;
}
--(this->len);
}
template <class T>
T Stack<T>::get_data()
{
return this->my_data[len-1];
}
template <class T>
bool Stack<T>::is_empty()
{
if(this->len==0){
return true;
}
return false;
}
main.cpp文件:
#include <iostream>
#include "stack.hpp"
using namespace std;
int main()
{
Stack<int> s(12);
s.push(1);
s.push(2);
s.push(3);
while(!s.is_empty()){
cout<<s.get_data()<<endl;
s.out_data();
}
return 0;
}
結果圖:
分析:因為這次我們把函數的實現放在了hpp文件里面,當我們調用頭文件的時候問編譯一次,還有就是當調用聲明的時候也會編譯一次,所以就達到了類模板的使用要求,所以這次我們就可以鏈接到。
5.3類模板的特化
#include <iostream>
using namespace std;
template<class T1>
class A
{
public:
A()
{
cout<<"A的基礎模板"<<endl;
}
};
template <>
class A <int>
{
public:
A()
{
cout<<"A的特化模板"<<endl;
}
};
template <class T2,class T3>
class B
{
public:
B()
{
cout<<"B的基礎模板"<<endl;
}
};
template <class T2>
class B<T2,float>
{
public:
B()
{
cout<<"B的偏化模板"<<endl;
}
};
int main()
{
A<float> a;
A<int> a1;
B<int ,int> b1;
B<int ,float> b2;
return 0;
}
結果圖:
分析:當使用類模板去定義對象時,因為具體指定使參數類型,所以他將優先調用與之指定類型相匹配的特化或偏特化版本。否則,將直接調用全特化。
5.4C++中類模板中的內嵌類
內嵌類一般情況下是為外圍類而服務:比如說:STL容器中所提供的迭代器就是一種內嵌類。內嵌類并不是對外公開的,只做為外圍類的一個輔助類。隱藏在外圍類的內部,對外不可以見。只能通過::域名訪問的形式,才能訪問到。
代碼實例:
#include <iostream>
using namespace std;
template <typename T>
class A
{
public:
class B
{
int a = 100;
int b = 200;
static B* c;
};
};
template <class T>
typename A<T>::B* A<T>::B::c = nullptr;
int main()
{
A<int> a;
cout << sizeof (a) << endl;
A<float>::B b1;
cout << sizeof(b1) <<endl;
return 0;
}
內嵌類需要注意的幾點內容:
1.內嵌類可以訪問定義在外圍類(enclosing class)中的靜態實例變量。外圍類不可以訪問嵌套類的成員.
2.不能從內嵌類中訪問外部類的非靜態成員.
3.可以在外部通過作用域限定符調用.
原文鏈接:https://blog.csdn.net/a2998658795/article/details/126062413
相關推薦
- 2022-04-28 oracle重置序列從0開始遞增1_oracle
- 2023-04-24 python中argparse模塊及action='store_true'詳解_python
- 2022-10-03 React中路由參數如何改變頁面不刷新數據的情況_React
- 2022-09-03 Python?pandas?DataFrame基礎運算及空值填充詳解_python
- 2023-04-06 C語言順序表的基本操作(初始化,插入,刪除,查詢,擴容,打印,清空等)_C 語言
- 2023-11-14 docker啟動報錯:Job for docker. service failed because
- 2022-07-28 聊聊docker跨主機之間容器通信問題_docker
- 2022-07-30 Linux下查看文件和文件夾大小
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支