杭州临安网站建设,深圳高端网站设计,网站建设的er图怎么画,网站不收录的解决办法文章目录1.阐述模板的实例化和重复定义问题2.分离编译可能出现的问题3.解决方法将函数模板的定义放到头文件中模板定义的位置显式实例化模板总结1.阐述模板的实例化和重复定义问题
C模板是一种非常强大的工具#xff0c;可以为我们提供通用的代码实现方式。然鹅#xff0c;在…
文章目录1.阐述模板的实例化和重复定义问题2.分离编译可能出现的问题3.解决方法将函数模板的定义放到头文件中模板定义的位置显式实例化模板总结1.阐述模板的实例化和重复定义问题
C模板是一种非常强大的工具可以为我们提供通用的代码实现方式。然鹅在使用模板时会涉及到模板的实例化和重复定义的问题。为了避免这些问题并提高编译效率C提供了模板分离编译的机制。 1.模板为什么会涉及到实例化和重复定义的问题 C的模板时一种通用的代码实现方式可以根据不同的类型参数生成具体的代码实例。当程序使用一个模板时编译器将根据其具体的类型参数生成对应的代码实例这个过程称为模板的实例化。
在模板实例化时编译器会根据模板定义生成对应的函数或类并在程序中调用或实例化这些函数或类。然鹅由于模板的定义通常都放在头文件中当多个源文件包含相同的头文件时就会出现重复定义的问题。
以下是一个简单的示例代码演示了在两个源文件中包含相同的头文件时引起的重复定义错误且该头文件中定义了一个函数模板
// add.h
templatetypename T
T add(T a, T b) {return a b;
}
// main1.cpp
#include add.hint main() {int a 1, b 2;int c add(a, b);return 0;
}
// main2.cpp
#include add.hint main() {double a 1.5, b 2.5;double c add(a, b); // error: redefinition of addreturn 0;
}在上述示例代码中我们定义了一个名为add的函数模板并在两个不同的源文件中分别包含相同的头文件add.h。当编译器在对这两个源文件进行编译时它会对同一个函数模板进行多次实例化从而导致重复定义错误。
error: redefinition of add2.但是它们实例化出的是两个不同类型的add函数呀为什么有重复定义问题呢 是的没错。实例化出来的两个add函数一个是int类型的另一个是double类型的。但是这并不是导致重复定义问题的根本原因。
在C中函数模板的定义通常都放在头文件中而头文件可能被多个源文件包含当多个源文件包含相同的头文件时其中的函数模板定义也会被多次包含从而引发重定义问题。
具体地说在上述实例中编译器会对头文件add.h进行两次编译并生成两个不同的目标文件。然后编译器试图将两个目标文件链接到一起时就会发现它们之间存在重复定义的符号从而导致连接错误。重复定义的符号指的是在多个目标文件中都存在名称相同但实体不同的符号。在C中符号通常是函数名变量名类名。
在上述示例中我们定义了一个名为add的函数模板在两个不同的源文件对其进行了示例化。当编译器将这两个源文件编译成目标文件时它们分别包含了一个名为addint和adddouble这个符号打不出来见谅的符号。然鹅当我们试图将这两个目标文件链接到一起时就会发现这两个符号名称相同但实体不同所以会导致链接错误。
2.分离编译可能出现的问题
开头说过在使用模板时会涉及到模板的实例化和重复定义的问题。为了避免这些问题并提高编译效率C提供了模板分离编译的机制。 1.什么是分离编译模式 一个项目由若干个源文件共同实现而每个源文件单独编译生成目标文件最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译。 2.模板的分离编译可能会出现的问题分析 在C中模板分离编译是指将模板的声明和定义分开存放在不同的文件中以避免多个源文件中重复定义同一个模板的问题。然而在实践中模板分离编译常常会带来一些问题主要包括以下几个方面
链接错误由于模板被分成了多个文件如果链接时遗漏了某个模板的定义就会导致链接错误。多次实例化由于模板的定义通常都写在头文件中如果多个源文件包含相同的头文件就可能导致同一个模板被多次实例化从而增加编译时间和代码大小。可读性差模板分离编译会导致模板的声明和定义分散在不同的文件中使得代码的可读性变差。
我们都知道程序运行起来一般要以下4个步骤
预处理头文件展开去注释宏替换条件编译等。编译检查代码的规范性是否有语法错误等确定代码实际要做的工作在检查无误后将代码翻译成汇编语言。汇编把编译阶段生成的文件转成目标文件obj链接将生成的各个目标文件进行链接生成可执行文件。
多次实例化的问题我们在前面已经讲解的很清楚了我们现在来认识一下在模板的分离编译中可能会遇到的链接问题。
在C程序设计中在一个源文件中定义某个函数然后在另一个源文件中使用该函数这是一种非常普遍的做法。但是如果定义和调用一个函数模板时也采用这种方式会发生编译错误。下面的程序由三个文件组成add.h用来对函数模板进行申明add.cpp用来定义函数模板main.cpp包含add.h头文件并调用相应的函数模板。
// a.h
templateclass T
T Add(const T left, const T right);// a.cpp
templateclass T
T Add(const T left, const T right)
{
return left right;
}// main.cpp
#includea.h
int main()
{
Add(1, 2);
Add(1.0, 2.0);
return 0;
}这是一个结构非常清晰的程序但是它不能通过编译。在VS2017下的出错信息是 这是很典型的链接问题那么原因就出在分离编译的模式上。在分离编译模式下a.cpp会生成一个目标文件a.obj由于在a,cpp文件中并没有发生函数模板调用所以不会把函数模板示例化成int或double类型那么在a.obj中就找不到模板函数的实现代码所以在链接时就会出现错误。
在main.cpp中虽然函数模板被调用但是由于没有模板代码也不能将其实例化。在main.obj中找不到模板函数int add(…),在链接时就会出现函数未定义的错误。
3.解决方法
将函数模板的定义放到头文件中
将声明和定义放到一个文件 “xxx.hpp” 里面或者xxx.h其实也是可以的。推荐使用这种。这样的话只要包含了这个头文件就会把函数模板的代码包含进来若发生函数调用可以直接依据类型进行实例化。这种方法比较推荐但是也有不足之处。
将函数定义写在头文件中暴露了函数的实现细节。不符合分离编译模式的规则。
模板定义的位置显式实例化
解决代码如下
//Add.h
#includeiostream
using namespace std;
//函数模板声明
templateclass T
T Add(const T x,const T y);//Add.cpp
templateclass T
T Add(const T x,const T y)
{return xy;
}
//显示实例化
template int Add(const int x,const int y);
template double Add(const double x,const double y);//main.cpp
#includeAdd.h
int main()
{//调用函数模板实例化的函数coutAdd(10,20)endl;coutAdd(10.2,10.2)endl;return 0;
}上述代码在Add.cpp文件中对函数模板进行显示实例化这样函数模板就可以生成对应的函数这样再链接时就不会出错了。 除了这两种方法还有其它方法如 1.使用模板库通过将模板定义封装到库文件中可以避免多个源文件中对同一模板的重复定义并提高代码重用性。 2.模板导出export在模板声明时使用export关键字可以告诉编译器只生成一个模板示例避免多次实例化统一模板。 3.内联函数将模板函数定义为内联函数可以避免链接错误同时减少函数调用的开销。 模板总结
【优点】
模板复用了代码节省资源更快的迭代开发C的标准模板库STL因此而产生。增强了代码的灵活性。
【缺陷】
模板会导致代码膨胀问题也会导致编译时间变长。出现模板编译错误时错误信息非常凌乱不易定位。