從彙編角度看C++模板

  • 1. 模板函數         

        模板函數定義的就是一種函數。既然是函數,那麼就有輸入數據和輸出數據。和模板類的概念差不多,模板函數的初衷也是爲了在函數操作上抽取共同的特性,屏蔽的是類型的不同和差異。

        模板函數的反彙編示例:

    

#include <stdio.h>
#include <stdlib.h>

template <typename type>
type compare(type a, type b)
{
    return a > b ? a : b;
};

int main(void) {
	int v = compare(2, 3);
	double v2 = compare(2.5, 3.5);
	printf("v=%d,v2=%f", v, v2);
}

 

 

     

           main():
080483e4:   push %ebp
080483e5:   mov %esp,%ebp
080483e7:   and $0xfffffff0,%esp
080483ea:   sub $0x20,%esp
11        	int v = compare(2, 3);
080483ed:   movl $0x3,0x4(%esp)
080483f5:   movl $0x2,(%esp)
080483fc:   call 0x8048444 <compare<int>(int, int)>
08048401:   mov %eax,0x1c(%esp)
12        	double v2 = compare(2.5, 3.5);
08048405:   fldl 0x8048580
0804840b:   fstpl 0x8(%esp)
0804840f:   fldl 0x8048588
08048415:   fstpl (%esp)
08048418:   call 0x8048459 <compare<double>(double, double)>
0804841d:   fstpl 0x10(%esp)
13        	printf("v=%d,v2=%f", v, v2);
08048421:   fldl 0x10(%esp)
08048425:   fstpl 0x8(%esp)
08048429:   mov 0x1c(%esp),%eax
0804842d:   mov %eax,0x4(%esp)
08048431:   movl $0x8048570,(%esp)
08048438:   call 0x8048300 <printf@plt>
0804843d:   mov $0x0,%eax
14        }
08048442:   leave 
08048443:   ret 
 5        type compare(type a, type b)
          compare<int>(int, int):
08048444:   push %ebp
08048445:   mov %esp,%ebp
 7            return a > b ? a : b;
08048447:   mov 0x8(%ebp),%eax
0804844a:   cmp 0xc(%ebp),%eax
0804844d:   jle 0x8048454 <compare<int>(int, int)+16>
0804844f:   mov 0x8(%ebp),%eax
08048452:   jmp 0x8048457 <compare<int>(int, int)+19>
08048454:   mov 0xc(%ebp),%eax
 8        };
08048457:   pop %ebp
08048458:   ret 
 5        type compare(type a, type b)
          compare<double>(double, double):
08048459:   push %ebp
0804845a:   mov %esp,%ebp
0804845c:   sub $0x10,%esp
0804845f:   mov 0x8(%ebp),%eax
08048462:   mov %eax,-0x8(%ebp)
08048465:   mov 0xc(%ebp),%eax
08048468:   mov %eax,-0x4(%ebp)
0804846b:   mov 0x10(%ebp),%eax
0804846e:   mov %eax,-0x10(%ebp)
08048471:   mov 0x14(%ebp),%eax
08048474:   mov %eax,-0xc(%ebp)
 7            return a > b ? a : b;
08048477:   fldl -0x8(%ebp)
0804847a:   fldl -0x10(%ebp)
0804847d:   fxch %st(1)
0804847f:   fucomip %st(1),%st
08048481:   fstp %st(0)
08048483:   seta %al
08048486:   test %al,%al
08048488:   je 0x804848f <compare<double>(double, double)+54>
0804848a:   fldl -0x8(%ebp)
0804848d:   jmp 0x8048492 <compare<double>(double, double)+57>
0804848f:   fldl -0x10(%ebp)
 8        };
08048492:   leave 
08048493:   ret 

 

     彙編代碼表明,兩個compare調用的函數地址並不是一致的。其中整數的compare地址是0x8048444,而double的地址是 0x8048459。這說明編譯器在編譯的時候幫我們同時生成了兩個compare函數。所以說,模板類的本質就是在編譯器增加判斷處理工作的同時,減 少手工的重複勞動。模板函數不需要顯示定義函數的參數類型,這是因爲可以從入參判斷出函數的類型。

 

  •  2. 模板類
   
#include <stdio.h>
#include <stdlib.h>

template<typename type>
class data_process {
	type a;
	type b;
public:
	data_process(type m, type n) :
			a(m), b(n) {
	}

	~data_process() {
	}
	type add() {
		return a + b;
	}
	type sub() {
		return a - b;
	}
};

int main(void) {
	data_process<int> data1(1,2);
	data_process<double> data2(1.2, 2.3);
	printf("v1=%d,v2=%f\n", data1.add(), data2.add());
}
 
          main():
080484f4:   push %ebp
080484f5:   mov %esp,%ebp
080484f7:   push %ebx
080484f8:   and $0xfffffff0,%esp
080484fb:   sub $0x50,%esp
24        	data_process<int> data1(1,2);
080484fe:   movl $0x2,0x8(%esp)
08048506:   movl $0x1,0x4(%esp)
0804850e:   lea 0x48(%esp),%eax
08048512:   mov %eax,(%esp)
08048515:   call 0x80485b4 <data_process<int>::data_process(int, int)>
25        	data_process<double> data2(1.2, 2.3);
0804851a:   fldl 0x8048710
08048520:   fstpl 0xc(%esp)
08048524:   fldl 0x8048718
0804852a:   fstpl 0x4(%esp)
0804852e:   lea 0x38(%esp),%eax
08048532:   mov %eax,(%esp)
08048535:   call 0x80485d0 <data_process<double>::data_process(double, double)>
26        	printf("v1=%d,v2=%f\n", data1.add(), data2.add());
0804853a:   lea 0x38(%esp),%eax
0804853e:   mov %eax,(%esp)
08048541:   call 0x804861a <data_process<double>::add()>
08048546:   fstpl 0x28(%esp)
0804854a:   lea 0x48(%esp),%eax
0804854e:   mov %eax,(%esp)
08048551:   call 0x8048608 <data_process<int>::add()>
08048556:   fldl 0x28(%esp)
0804855a:   fstpl 0x8(%esp)
0804855e:   mov %eax,0x4(%esp)
08048562:   movl $0x8048700,(%esp)
08048569:   call 0x8048410 <printf@plt>
0804856e:   lea 0x38(%esp),%eax
08048572:   mov %eax,(%esp)
08048575:   call 0x8048602 <data_process<double>::~data_process()>
0804857a:   lea 0x48(%esp),%eax
0804857e:   mov %eax,(%esp)
08048581:   call 0x80485ca <data_process<int>::~data_process()>
08048586:   mov $0x0,%eax
27        }
0804858b:   mov -0x4(%ebp),%ebx
0804858e:   leave 
0804858f:   ret 
08048590:   mov %eax,%ebx
26        	printf("v1=%d,v2=%f\n", data1.add(), data2.add());
08048592:   lea 0x38(%esp),%eax
08048596:   mov %eax,(%esp)
08048599:   call 0x8048602 <data_process<double>::~data_process()>
0804859e:   lea 0x48(%esp),%eax
080485a2:   mov %eax,(%esp)
080485a5:   call 0x80485ca <data_process<int>::~data_process()>
080485aa:   mov %ebx,%eax
080485ac:   mov %eax,(%esp)
080485af:   call 0x8048430 <_Unwind_Resume@plt>
 9        	data_process(type m, type n) :
          data_process<int>::data_process(int, int):
080485b4:   push %ebp
080485b5:   mov %esp,%ebp
10        			a(m), b(n) {
080485b7:   mov 0x8(%ebp),%eax
080485ba:   mov 0xc(%ebp),%edx
080485bd:   mov %edx,(%eax)
080485bf:   mov 0x8(%ebp),%eax
080485c2:   mov 0x10(%ebp),%edx
080485c5:   mov %edx,0x4(%eax)
11        	}
080485c8:   pop %ebp
080485c9:   ret 
13        	~data_process() {
          data_process<int>::~data_process():
080485ca:   push %ebp
080485cb:   mov %esp,%ebp
14        	}
080485cd:   pop %ebp
080485ce:   ret 
080485cf:   nop 
 9        	data_process(type m, type n) :
          data_process<double>::data_process(double, double):
080485d0:   push %ebp
080485d1:   mov %esp,%ebp
080485d3:   sub $0x10,%esp
080485d6:   mov 0xc(%ebp),%eax
080485d9:   mov %eax,-0x8(%ebp)
080485dc:   mov 0x10(%ebp),%eax
080485df:   mov %eax,-0x4(%ebp)
080485e2:   mov 0x14(%ebp),%eax
080485e5:   mov %eax,-0x10(%ebp)
080485e8:   mov 0x18(%ebp),%eax
080485eb:   mov %eax,-0xc(%ebp)
10        			a(m), b(n) {
080485ee:   mov 0x8(%ebp),%eax
080485f1:   fldl -0x8(%ebp)
080485f4:   fstpl (%eax)
080485f6:   mov 0x8(%ebp),%eax
080485f9:   fldl -0x10(%ebp)
080485fc:   fstpl 0x8(%eax)
11        	}
080485ff:   leave 
08048600:   ret 
08048601:   nop 
13        	~data_process() {
          data_process<double>::~data_process():
08048602:   push %ebp
08048603:   mov %esp,%ebp
14        	}
08048605:   pop %ebp
08048606:   ret 
08048607:   nop 
15        	type add() {
          data_process<int>::add():
08048608:   push %ebp
08048609:   mov %esp,%ebp
16        		return a + b;
0804860b:   mov 0x8(%ebp),%eax
0804860e:   mov (%eax),%edx
08048610:   mov 0x8(%ebp),%eax
08048613:   mov 0x4(%eax),%eax
08048616:   add %edx,%eax
17        	}
08048618:   pop %ebp
08048619:   ret 
15        	type add() {
          data_process<double>::add():
0804861a:   push %ebp
0804861b:   mov %esp,%ebp
16        		return a + b;
0804861d:   mov 0x8(%ebp),%eax
08048620:   fldl (%eax)
08048622:   mov 0x8(%ebp),%eax
08048625:   fldl 0x8(%eax)
08048628:   faddp %st,%st(1)
17        	}
0804862a:   pop %ebp
0804862b:   ret 
      針對每一個類型,模板的構造函數、析構函數、成員函數都要獨立生成,這從上面的函數地址就可以看出來。所以模板的本質就是對 不同數據類型的相似性操作進行共同屬性提取,合成模板。在應用的時候,編譯器根據我們使用中的數據類型獨立生成每一個類,構建每一個基本運算變量和運算函 數,僅此而已。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章