目录模版基础知识点详见:【C++】详谈模版
- 模版的分离编译
- 模版能不能多文件编程?
- 分离编译
- 编译器工作步骤
- C/C++的分离编译
- 如何解决模版分离编译问题
- 多文件编程
- 对于函数
- 对于类
- 类内定义的成员函数是内联函数(inline)
模版的分离编译 模版能不能多文件编程?
编程的好习惯是高内聚,低耦合。我们通常会通过多文件编写程序实现模块化编程。
所谓多文件编程,就是把不同功能的函数封装到不同的文件中。(比如一个.cpp源文件和一个.h头文件被称为一个模块)
具体可以参考这个:自定义实现的顺序表类多文件编程代码
现在自定义一个函数模版,并且采用多文件编程:
头文件 template_test.h
// 测试模版多文件 #pragma once // 声明一个函数模版 templateT Add(const T& left, const T& right);
函数文件 template_test.cpp
#include "template_test.h" // 定义这个函数模版 templateT Add(const T& left, const T& right) { return left + right; }
源文件 main.cpp
#include "template_test.h" #includeusing namespace std; int main() { cout << Add(1, 2) << endl; return 0; }
上面的代码结果一定报错!因为C/C++采用的是分离编译
一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。
对于普通的多文件函数:
对于模版来说,模版未调用前不会实例化 !因此上面的例子会报错。下面是上例图解:
分离编译是C/C++编译的特性,我们无法避免。因此,对于模版来说,我们可以不让其多文件编程。
但一个模版如果被多个源文件使用,不多文件处理会非常麻烦,因此,我们可以将模版的定义直接放在.h 或 .hpp(推荐)头文件中
.hpp头文件指.h文件与.cpp文件的合并,.hpp文件是一般模板类的头文件,VCL 专用的头文件
.h文件一般只放函数的声明,若声明与定义不能分开(模版),最好定义在.hpp文件(虽然.h文件也能实现,但.hpp文件可以让使用者区分带函数定义的头文件和普通头文件)
实际应用详见:自定义链表模版类
如果将模版直接定义在头文件中,会不会产生函数的重定义问题?
- 答案:不会
- 即使多个源文件引用了一个模版的.hpp头文件,并且在独自编译时实例了同样的类型函数,编译器在链接时会自动屏蔽相同类型的模版实例,只会保留一份(这是模版的特性)
多文件编程 对于函数
| 头文件(.h):函数声明;函数文件(.cpp):函数定义 | 头文件(.h/.hpp):直接实现函数的定义 | |
|---|---|---|
| 函数 | √ (推荐使用) | ×(多次引用的函数重定义问题) |
| 函数模版 | ×(模版不支持分离编译) | √(相同模版实例,链接时只会保留一份) |
| 头文件(.h):定义类、声明成员函数;函数文件(.cpp):定义成员函数 | 头文件(.h):定义类、类中定义成员函数;(不推荐) | 头文件(.h/.hpp):定义类、类外定义成员函数; | |
|---|---|---|---|
| 类 | √(推荐使用) | √(类中的成员函数会被认为是inline函数) | ×(多次引用的函数重定义问题) |
| 类模版 | ×(模版不支持分离编译) | √(类中的成员函数会被认为是inline函数) | √(相同模版实例,链接时只会保留一份) |
什么是内联函数:
- inline函数就是将当前函数直接就地展开,使其无法跨文件调用(普通函数是整个工程可见,而inline函数只有当前文件可见);
- 在头文件声明的inline函数,别的源文件定义inline函数,只包含该头文件无法找到该函数入口地址,因为inline函数没有入口地址;
内联函数直接定义在头文件的特性:
- #include该头文件的源文件相当于直接在本地展开这个函数,故不会出现多文件调用同一头文件导致的函数重定义的情况;
- 类内直接定义成员函数在头文件就是利用了内联函数的这个特性,方便了类的定义;
- 因此如果一定要使用inline函数,最好直接定义在头文件中,而不是定义在使用该函数的源文件中,方便不同文件调用;
内联函数优缺点:
- C++中支持内联函数,其目的是为了提高函数的执行效率,不会函数调用从而减少程序运行时间;
- 但 inline函数会使程序规模增大,故不建议头文件直接在类中定义成员函数;



