c++函式模板

it_xiangqiang發表於2020-10-16

1為什麼要有函式模板
需求:寫n個函式,交換char型別、int型別、double型別變數的值。
案例:
#include
using namespace std;
/*
void myswap(int &a, int &b)
{
int t = a;
a = b;
b = t;
}

void myswap(char &a, char &b)
{
char t = a;
a = b;
b = t;
}
*/

//template 關鍵字告訴C++編譯器 我要開始泛型了.你不要隨便報錯
//資料型別T 引數化資料型別
template
void myswap(T &a, T &b)
{
T t;
t = a;
a = b;
b = t;
}

void main()
{
//char a = ‘c’;
int x = 1;
int y = 2;
myswap(x, y); //自動資料型別 推導的方式
float a = 2.0;
float b = 3.0;
myswap(a, b); //自動資料型別 推導的方式
myswap(a, b); //顯示型別呼叫
cout<<“hello…”<<endl;
system(“pause”);
return ;
}

2函式模板語法
函式模板定義形式
template < 型別形式參數列 >
型別形式引數的形式為:
typename T1 , typename T2 , …… , typename Tn
或 class T1 , class T2 , …… , class Tn

在這裡插入圖片描述
函式模板呼叫
myswap(a, b); //顯示型別呼叫
myswap(a, b); //自動資料型別推導

3函式模板和模板函式
在這裡插入圖片描述

4函式模板做函式引數
#include
using namespace std;

template<typename T, typename T2>
void sortArray(T *a, T2 num)
{
T tmp ;
int i, j ;
for (i=0; i<num; i++)
{
for (j=i+1; j<num; j++)
{
if (a[i] < a[j])
{
tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
}
}
}

template
void pirntArray(T *a, int num)
{
int i = 0;
for (i=0; i<num; i++)
{
cout<<a[i]<<" ";
}
}

void main()
{
int num = 0;
char a[] = “ddadeeettttt”;
num = strlen(a);
printf(“排序之前\n”);
pirntArray(a, num);
sortArray<char, int>(a, num); //顯示型別呼叫 模板函式 <>
printf(“排序之後\n”);
pirntArray(a, num);
cout<<“hello…”<<endl;
system(“pause”);
return ;
}
5函式模板遇上函式過載
函式模板和普通函式區別結論:
/*
函式模板不允許自動型別轉化
普通函式能夠進行自動型別轉換
/
函式模板和普通函式在一起,呼叫規則:
/

1 函式模板可以像普通函式一樣被過載
2 C++編譯器優先考慮普通函式
3 如果函式模板可以產生一個更好的匹配,那麼選擇模板
4 可以通過空模板實參列表的語法限定編譯器只通過模板匹配
*/
案例1:
#include
using namespace std;

template
void myswap(T &a, T &b)
{
T t;
t = a;
a = b;
b = t;
cout<<“myswap 模板函式do”<<endl;
}

void myswap(char &a, int &b)
{
int t;
t = a;
a = b;
b = t;
cout<<“myswap 普通函式do”<<endl;
}

void main()
{
char cData = ‘a’;
int iData = 2;
//myswap(cData, iData); //結論 函式模板不提供隱式的資料型別轉換 必須是嚴格的匹配
myswap(cData, iData);
//myswap(iData, cData);
cout<<“hello…”<<endl;
system(“pause”);
return ;
}

案例2:

#include “iostream”
using namespace std;

int Max(int a, int b)
{
cout<<“int Max(int a, int b)”<<endl;
return a > b ? a : b;
}

template
T Max(T a, T b)
{
cout<<“T Max(T a, T b)”<<endl;
return a > b ? a : b;
}

template
T Max(T a, T b, T c)
{
cout<<“T Max(T a, T b, T c)”<<endl;
return Max(Max(a, b), c);
}

void main()
{
int a = 1;
int b = 2;
cout<<Max(a, b)<<endl; //當函式模板和普通函式都符合呼叫時,優先選擇普通函式
cout<<Max<>(a, b)<<endl; //若顯示使用函式模板,則使用<> 型別列表
cout<<Max(3.0, 4.0)<<endl; //如果 函式模板產生更好的匹配 使用函式模板
cout<<Max(5.0, 6.0, 7.0)<<endl; //過載
cout<<Max(‘a’, 100)<<endl; //呼叫普通函式 可以隱式型別轉換
system(“pause”);
return ;
}

6 C++編譯器模板機制剖析
思考:為什麼函式模板可以和函式過載放在一塊。C++編譯器是如何提供函式模板機制的?
編譯器編譯原理
什麼是gcc
gcc(GNU C Compiler)編譯器的作者是Richard Stallman,也是GNU專案的奠基者。
什麼是gcc:gcc是GNU Compiler Collection的縮寫。最初是作為C語言的編譯器(GNU C Compiler),現在已經支援多種語言了,如C、C++、Java、Pascal、Ada、COBOL語言等。
gcc支援多種硬體平臺,甚至對Don Knuth 設計的 MMIX 這類不常見的計算機都提供了完善的支援

gcc主要特徵
1)gcc是一個可移植的編譯器,支援多種硬體平臺
2)gcc不僅僅是個本地編譯器,它還能跨平臺交叉編譯。
3)gcc有多種語言前端,用於解析不同的語言。
4)gcc是按模組化設計的,可以加入新語言和新CPU架構的支援
5)gcc是自由軟體

gcc編譯過程
預處理(Pre-Processing)
編譯(Compiling)
彙編(Assembling)
連結(Linking)
Gcc *.c –o 1exe (總的編譯步驟)
Gcc –E 1.c –o 1.i //巨集定義 巨集展開
Gcc –S 1.i –o 1.s
Gcc –c 1.s –o 1.o
Gcc 1.o –o 1exe
結論:gcc編譯工具是一個工具鏈。。。。
在這裡插入圖片描述

hello程式是一個高階C語言程式,這種形式容易被人讀懂。為了在系統上執行hello.c程式,每條C語句都必須轉化為低階機器指令。然後將這些指令打包成可執行目標檔案格式,並以二進位制形式儲存器於磁碟中。
gcc常用編譯選項
在這裡插入圖片描述
練習
gcc -E hello.c -o hello.i(預處理)
gcc -S hello.i -o hello.s(編譯)
gcc -c hello.s -o hello.o(彙編)
gcc hello.o -o hello(連結)
以上四個步驟,可合成一個步驟
gcc hello.c -o hello(直接編譯連結成可執行目標檔案)
gcc -c hello.c或gcc -c hello.c -o hello.o(編譯生成可重定位目標檔案)
建議初學都加這個選項。下面這個例子如果不加-Wall選項編譯器不報任何錯誤,但是得到的結果卻不是預期的。
#include <stdio.h>
int main(void)
{
printf(“2+1 is %f”, 3);
return 0;
}
Gcc編譯多個.c
hello_1.h
hello_1.c
main.c
一次性編譯
gcc hello_1.c main.c –o newhello
獨立編譯
gcc -Wall -c main.c -o main.o
gcc -Wall -c hello_1.c -o hello_fn.o
gcc -Wall main.o hello_1.o -o newhello

模板函式反彙編觀察
命令:g++ -S 7.cpp -o 7.s
.file “7.cpp”
.text
.def __ZL6printfPKcz; .scl 3; .type 32; .endef
__ZL6printfPKcz:
LFB264:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
pushl %ebx
subl $36, %esp
.cfi_offset 3, -12
leal 12(%ebp), %eax
movl %eax, -12(%ebp)
movl -12(%ebp), %eax
movl %eax, 4(%esp)
movl 8(%ebp), %eax
movl %eax, (%esp)
call ___mingw_vprintf
movl %eax, %ebx
movl %ebx, %eax
addl $36, %esp
popl %ebx
.cfi_restore 3
popl %ebp
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
LFE264:
.lcomm __ZStL8__ioinit,1,1
.def ___main; .scl 2; .type 32; .endef
.section .rdata,“dr”
LC0:
.ascii “a:%d b:%d \12\0”
LC1:
.ascii “c1:%c c2:%c \12\0”
LC2:
.ascii “pause\0”
.text
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
LFB1023:
.cfi_startproc
.cfi_personality 0,___gxx_personality_v0
.cfi_lsda 0,LLSDA1023
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
andl $-16, %esp
subl $32, %esp
call ___main
movl $0, 28(%esp)
movl $10, 24(%esp)
movb $97, 23(%esp)
movb $98, 22(%esp)
leal 24(%esp), %eax
movl %eax, 4(%esp)
leal 28(%esp), %eax
movl %eax, (%esp)
call _Z6myswapIiEvRT_S1 //66 ===>126
movl 24(%esp), %edx
movl 28(%esp), %eax
movl %edx, 8(%esp)
movl %eax, 4(%esp)
movl $LC0, (%esp)
call __ZL6printfPKcz
leal 22(%esp), %eax
movl %eax, 4(%esp)
leal 23(%esp), %eax
.file “7.cpp”
.text
.def __ZL6printfPKcz; .scl 3; .type 32; .endef
__ZL6printfPKcz:
LFB264:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
pushl %ebx
subl $36, %esp
.cfi_offset 3, -12
leal 12(%ebp), %eax
movl %eax, -12(%ebp)
movl -12(%ebp), %eax
movl %eax, 4(%esp)
movl 8(%ebp), %eax
movl %eax, (%esp)
call ___mingw_vprintf
movl %eax, %ebx
movl %ebx, %eax
addl $36, %esp
popl %ebx
.cfi_restore 3
popl %ebp
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
LFE264:
.lcomm __ZStL8__ioinit,1,1
.def ___main; .scl 2; .type 32; .endef
.section .rdata,“dr”
LC0:
.ascii “a:%d b:%d \12\0”
LC1:
.ascii “c1:%c c2:%c \12\0”
LC2:
.ascii “pause\0”
.text
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
LFB1023:
.cfi_startproc
.cfi_personality 0,___gxx_personality_v0
.cfi_lsda 0,LLSDA1023
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
andl $-16, %esp
subl $32, %esp
call ___main
movl $0, 28(%esp)
movl $10, 24(%esp)
movb $97, 23(%esp)
movb $98, 22(%esp)
leal 24(%esp), %eax
movl %eax, 4(%esp)
leal 28(%esp), %eax
movl %eax, (%esp)
call _Z6myswapIiEvRT_S1 //66 ===>126
movl 24(%esp), %edx
movl 28(%esp), %eax
movl %edx, 8(%esp)
movl %eax, 4(%esp)
movl $LC0, (%esp)
call __ZL6printfPKcz
leal 22(%esp), %eax
movl %eax, 4(%esp)
leal 23(%esp), %eax
movl %eax, (%esp)
call _Z6myswapIcEvRT_S1 //77 ===>155
movzbl 22(%esp), %eax
movsbl %al, %edx
movzbl 23(%esp), %eax
movsbl %al, %eax
movl %edx, 8(%esp)
movl %eax, 4(%esp)
movl $LC1, (%esp)
call __ZL6printfPKcz
movl $LC2, (%esp)
LEHB0:
call _system
LEHE0:
movl KaTeX parse error: Expected group after '_' at position 53: …) LEHB1: call _̲_Unwind_Resume …Z6myswapIiEvRT_S1,“x”
.linkonce discard
.globl _Z6myswapIiEvRT_S1
.def _Z6myswapIiEvRT_S1; .scl 2; .type 32; .endef
_Z6myswapIiEvRT_S1: //126
LFB1024:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl 16 , m o v l 8 ( m o v l ( m o v l m o v l 12 ( m o v l ( m o v l 8 ( m o v l m o v l 12 ( m o v l − 4 ( m o v l l e a v e . c f i r e s t o r e 5. c f i d e f c f a 4 , 4 r e t . c f i e n d p r o c L F E 1024 : . s e c t i o n . t e x t 16, %esp movl 8(%ebp), %eax movl (%eax), %eax movl %eax, -4(%ebp) movl 12(%ebp), %eax movl (%eax), %edx movl 8(%ebp), %eax movl %edx, (%eax) movl 12(%ebp), %eax movl -4(%ebp), %edx movl %edx, (%eax) leave .cfi_restore 5 .cfi_def_cfa 4, 4 ret .cfi_endproc LFE1024: .section .text 16,movl8(movl(movlmovl12(movl(movl8(movlmovl12(movl4(movlleave.cfirestore5.cfidefcfa4,4ret.cfiendprocLFE1024:.section.textZ6myswapIcEvRT_S1,“x”
.linkonce discard
.globl _Z6myswapIcEvRT_S1
.def _Z6myswapIcEvRT_S1; .scl 2; .type 32; .endef
_Z6myswapIcEvRT_S1: //155
LFB1025:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $16, %esp
movl 8(%ebp), %eax
movzbl (%eax), %eax
movb %al, -1(%ebp)
movl 12(%ebp), %eax
movzbl (%eax), %edx
movl 8(%ebp), %eax
movb %dl, (%eax)
movl 12(%ebp), %eax
movzbl -1(%ebp), %edx
movb %dl, (%eax)
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
LFE1025:
.text
.def ___tcf_0; .scl 3; .type 32; .endef
___tcf_0:
LFB1027:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $8, %esp
movl $__ZStL8__ioinit, %ecx
call __ZNSt8ios_base4InitD1Ev
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
LFE1027:
.def __Z41__static_initialization_and_destruction_0ii; .scl 3; .type 32; .endef
__Z41__static_initialization_and_destruction_0ii:
LFB1026:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $24, %esp
cmpl $1, 8(%ebp)
jne L11
cmpl $65535, 12(%ebp)
jne L11
movl $__ZStL8__ioinit, %ecx
call __ZNSt8ios_base4InitC1Ev
movl $___tcf_0, (%esp)
call _atexit
L11:
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
LFE1026:
.def __GLOBAL__sub_I_main; .scl 3; .type 32; .endef
__GLOBAL__sub_I_main:
LFB1028:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $24, %esp
movl $65535, 4(%esp)
movl $1, (%esp)
call __Z41__static_initialization_and_destruction_0ii
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
LFE1028:
.section .ctors,“w”
.align 4
.long __GLOBAL__sub_I_main
.ident “GCC: (rev2, Built by MinGW-builds project) 4.8.0”
.def ___mingw_vprintf; .scl 2; .type 32; .endef
.def _system; .scl 2; .type 32; .endef
.def __Unwind_Resume; .scl 2; .type 32; .endef
.def __ZNSt8ios_base4InitD1Ev; .scl 2; .type 32; .endef
.def __ZNSt8ios_base4InitC1Ev; .scl 2; .type 32; .endef
.def _atexit; .scl 2; .type 32; .endef

函式模板機制結論
編譯器並不是把函式模板處理成能夠處理任意類的函式
編譯器從函式模板通過具體型別產生不同的函式
編譯器會對函式模板進行兩次編譯
在宣告的地方對模板程式碼本身進行編譯;在呼叫的地方對引數替換後的程式碼進行編譯。

相關文章