c++类模板实例化验证

摘要:

1. 首先通过PEview工具观察一般的代码编译链接过程;

2. 通过PEview工具和IDA工具验证类模板函数的实例化过程;

 

正文:

首先,看看下面的简单代码例子:

文件1,Main.cpp:

---------------------------

#include “play.h”
#include <stdlib.h>
int main() 
{ 
	play();
	system("pause");
	return 0;
} 

文件2:play.h:

--------------------------------

#ifndef _play_h_
#define _play_h_
void play();
#endif


文件3:play.cpp:

-------------------------------

#include <stdio.h>
void play(){
	Printf(“I am the play\n”);
}

上面三个文件存放在vs2008一个工程文件夹下面,按照这种方式生成的话,采用PEview查看main.obj的话,如下:

 

第二种写法,我们把play和main整合到一个文件中:

main.cpp:

-------------------------------------

void play(){
	printf("i am the play");
}

int main() 
{
	play();

	system("pause");
	return 0;
} 

对上述代码进行编译,并且采用PEview查看生成的obj文件的话,可以发现:

对比上述的两者,可以发现前后少了一个.text标志,可以猜想得知:

         有代码定义的话,那么play这个会有Section Number的.text标志;

         如果没有定义的话,只是一个引用的话,那么play这个没有对应的标志;

小结:经过上面的实验性代码,我们知道每个.cpp文件会编译成为对应名字的.obj文件;

          如果某个.cpp中拥有某个函数的定义代码时,那么通过PEview查看的话,可以发现其对应的

          名字的Section Number是.text;反之,某个.cpp中只是调用某个函数,而没有定义的话,那么

          用PEview查看只能看到名字(如上的?play@@YAXXZ),但是对应的Section Number为空;

 

有了上面的经验,我们来看看类的函数的实例化过程。我们编译下面几个文件中的代码:

文件1:test.h:

template<class T> 
class A 
{ 
	public: 
	     void f(); //这里只是个声明
};

文件2:test.cpp:

#include "test.h"
#include <stdio.h>

template<class T> 
void A<T>::f() //模板的实现,但注意:不是具现
{ 
	printf(" in test\n");
}

文件3:main.cpp:

int main() 
{ 
	A<int> a; 
	a.f();

	system("pause");
	return 0;
}


在vs2008中F7编译上述文件,发现如下的错误:
call_template.obj : error LNK2019: 无法解析的外部符号"public: void __thiscall A<int>::f(void)" (?f@?$A@H@@QAEXXZ),该符号在函数_main 中被引用

也就是函数A<int>::f(void)在call_template.cpp中被引用,但却没有在其他obj文件中被定义。按照我们的设想,该函数的定义应该会出现在test.obj中,

通过PEview.exe查看test.obj文件,并没有发现?f@?$A@H@@QAEXXZ这个符号。这也意味着test.obj中关于A<T>::f()的代码并没有编译。

 

网上查找后,得知模板并不是直接编译为可执行代码,需要经过具象化的过程,然后才是代码的编译。根据c++标准,采用惰性具象化的机制,

即如果在实现该模板的.cpp文件中没有用到模板的具现体时,编译器就懒得去具现化。

 

作为验证,假设我们在test.cpp文件中增加相应的具现体代码,如下:

 

#include "test.h"
#include <stdio.h>
template<class T> 
void A<T>::f() //模板的实现,但注意:不是具现
{ 
	printf(" in test\n");
}

void showme()
{
	A<int> b;
	b.f();
}

此处的showme函数对模板进行具现化,编译器看到该具现体时,完成void A<int>::f()的编译。通过PEview查看test.obj,可以发现:

c++类模板实例化验证_第1张图片

在.text段,出现?f@?$A@H@@QAEXXZ代号的函数实现体。如此main函数在引用时,link函数就可以在test.obj中找到对应的实现。

解决上述模板编译问题的一个基本举措: 将test.h和test.cpp合在一起,也即模板的声明和其对应的实现应该放在一起。

如果采用IDA来查看A<int>::f()有没有被定义,就更直接了:
 c++类模板实例化验证_第2张图片
如上,在IDA 6.1版本中,打开test.obj文件后,直接可以在标签Exports中查看其导出的符号。

 

 

你可能感兴趣的:(C++,模板)