c++ obj文件
obj文件就是目标文件,是源程序经过编译程序编译后生成的
不能直接执行,需要连接程序连接后才能生成可执行文件,这样就能执行
一般由机器代码组成,但也可以是自己定义的一些伪指令代码(需有专门的解释程序对其进行解释执行)
连接程序
把目标代码和它所使用的库文件连接的程序
obj文件与exe文件的区别
编译
当前源代码编译成二进制目标文件(obj文件)
链接(link)
将生成的.obj文件与库文件.lib等文件链接,生成可执行文件(.exe文件)
project中每个cpp经编译成为obj(object)文件,所有obj文件和资源问价经连接(link)成为可执行文件,obj文件可称为目标文件或中间文件。obj文件只给出了程序的相对地址,而exe是绝对地址
现代编译器的主要工作流程
源程序(source code)→ 预处理器(preprocessor)→ 编译器(compiler)→ 汇编程序(assembler)→ 目标程序(object code)→ 连接器(链接器,Linker)→ 可执行程序(executables)
函数模板的声明和实现
函数模板的声明和实现一般都放在.h文件中。
模板是在需要的时候,才会去生成一个具体的实例化。模板本身是不会被执行的(模板本身不产生汇编指令),是模板生成的具体实例化才产生指令。
模板的实现为什么放在.h中
编译器一次只能处理一个单元,即一次处理一个cpp文件,实例化时需要看到该模块的完整定义,若头文件中只有声明,没有定义,编译器就无法实例化该模块,最终会导致链接(link)错误,所以放在头文件中。
关于这一点我再解释一下:其他的函数声明放在h文件,函数定义放在cpp文件的原因是在编译阶段只要保证每个单独生成obj的cpp文件在编译阶段是自身完整的,cpp中使用的函数,是可以在#include的头文件中直接查找到声明的,而模板函数如果只在h文件中声明模板,cpp中直接要使用实例化后的函数,需要的是实例化后的函数声明,就找不到了。所以就把函数模板的实例化放在编译阶段,编译阶段的cpp是单独运行的,不和其他的cpp产生关系,所以函数模板不能放在其他的cpp。
若你坚持不想放在.h中,试试include "xxx.cpp"这种奇葩的做法也是可以的。补充:为什么include "xxx.cpp"可以呢?因为include "xxx.cpp"相当于代码的复制粘贴,相当于把include "xxx.cpp"里的函数模板定义直接添加到当前cpp中,那当前cpp在编译阶段就是自身完整的了。
函数模板的来源与实例
重载函数使编程变得方便,因为对于执行类似操作的一组函数,只要记住一个函数名称即可。但是,每个函数都必须单独编写。例如,来看以下重载的 square 求平方函数:
int square(int number)
{
return number * number;
}
double square(double number)
{
return number * number;
}
这两个函数之间的唯一区别是它们的返回值及其形参的数据类型。在这种情况下,编写函数模板比重载函数更方便。函数模板允许程序员编写一个单独的函数定义,以处理许多不同的数据类型,而不必为每个使用的数据类型编写单独的函数。
函数模板不是实际的函数,而是编译器用于生成一个或多个函数的 “模具”。在编写函数模板时,不必为形参、返回值或局部变量指定实际类型,而是使用类型形参来指定通用数据类型。当编译器遇到对函数的调用时,它将检查其实参的数据类型,并生成将与这些数据类型配合使用的函数代码。
以下是一个 square 函数的函数模板:
template<class T>
T square(T number)
{
return number * number;
}
在上面的 square 函数模板示例中,只使用了一个名为 T 的形参(如果有更多的话,它们将用逗号分隔)。在此之后,除了使用类型形参代替实际的数据类型名称之外,其他的都可以像往常一样写入函数的定义。在本函数模板示例中,以下是其函数头:
T square(T number)
其中,T 是类型形参或通用数据类型。该函数头定义了一个 square 函数,它返回一个 T 类型的值,并使用了一个形参 number,这也是 T 类型的数字。
如前所述,编译器会检查对 square 的每次调用,并以适当的数据类型填充 T,例如,以下调用将使用 int 参数:
int y, x = 4;
y = square(x);
以上代码将导致编译器生成以下函数:
int square(int number)
{
return number * number;
}
但是,如果使用以下语句调用 square 函数:
double y, d = 6.2;
y = square(d);
那么编译器生成的函数将如下所示:
double square(double number)
{
return number * number;
}
下面的程序演示了该函数模式的用法:
// This program uses a function template.
#include <iostream>
#include <iomanip>
using namespace std;
// Template definition for square function
template <class T>
T square(T number)
{
return number * number;
}
int main()
{
cout << setprecision(5);
//Get an integer and compute its square
cout << "Enter an integer: ";
int iValue;
cin >> iValue;
// The compiler creates int square(int) at the first occurrence of a call to square with an int argument
cout << "The square is " << square(iValue);
// Get a double and compute its square
cout << "\nEnter a double: ";
double dValue;
cin >> dValue;
// The compiler creates double square(double)at the first
// occurrence of a call to square with a double argument
cout << "The square is " << square (dValue) << endl;
return 0;
}
程序输出结果:
注意:函数模板仅仅是函数的规范,本身并不会导致使用内存。当编译器遇到对模板函数的调用时,才会在内存中创建该函数的实际实例。
后记
关于template声明模板时用Typename还是Class,目前看到2种说法:
- 第一种说法: template 用于基础数据类型,typename 指类型名,T 可以取 char int double 等。template 用于类,T 可以取任何类。结构体可以用 typename 或 class
- 第二种说法:最早的时候只有class,后来统一用typename,但是还是可以兼容class。
由于目前还没用到,不知真伪,后续遇到这个问题再来补充。
来源/参考 致谢
本文来自于:
https://blog.csdn.net/qq_29339467/article/details/91633427
http://c.biancheng.net/view/1565.html
感谢2位原作者。