摘要:本文主要向大家介绍了C++语言的分离式编译模式为什么不支持模版,通过具体的代码向大家展示,希望对大家学习C++语言有所帮助。
本文主要向大家介绍了C++语言的分离式编译模式为什么不支持模版,通过具体的代码向大家展示,希望对大家学习C++语言有所帮助。
在我们做c++开发的过程中,通常我们写一个类就会有一个.h和.cpp文件,当我们需要调用一个函数或者引用一个变量时我们只需要包含声明这些函数或者变量的.h文件即可,当我们编译c++项目时,每个cpp文件被编译成一个.obj文件,在这些.obj文件只包含非本文件函数的声明而没有这些函数的二进制代码,当我们调用这些函数时会生成一个call指令去调用函数,当在自己的.obj文件里找不到关于该函数的二进制文件时,编译器会交给c++连接器linker,此时linker会在其他.obj文件里寻找关于这些函数真正的定义找到相关的二进制代码,然后执行该函数。
这样做有它的好处,首先我们一个项目不可能都写在一个文件里,这样有利于项目模块的划分,其次有利于我们快速的定位项目中的错误。
但是当分离式编译碰到c++模版的时候就不灵了,要知道模版在实例化之前是不会被编译成二进制的,当我们有一个c++模版类的时候,此类在实例化之前是不会被编译成二进制代码的,而且对于模版类的函数,如果我们没有在它所在的.obj文件里被调用是不会被实例化的,也就是说这些函数不会生成相应的二进制代码的,此时如果我们调用这些函数c++编译器就会给出我们最不愿看到的编译错误了。因此我们不得不把模版类的定义和声明放在同个一个文件里。如果我们非要分开呢。
下面我们看一个模版例子
[cpp] view plain copy 1. // 2. // Header.h 3. // Demo 4. // 5. // Created by 杜国超 on 17/2/15. 6. // Copyright © 2017年 杜国超. All rights reserved. 7. // 8. 9. #ifndef Header_h 10. #define Header_h 11. #include <array> 12. #include <string> 13. 14. #define SIXDAY_RANK_NUM 10 15. 16. template<class t=""> 17. class TemplateClassA 18. { 19. public: 20. TemplateClassA(); 21. void init(T Id,int iScore); 22. void reset(); 23. public: 24. T mId; 25. int miScore; 26. }; 27. 28. template<unsigned int="" uiranknum,class="" t=""> 29. class TemplateClassB 30. { 31. public: 32. TemplateClassB(); 33. ~TemplateClassB(); 34. void reset(); 35. void say(); 36. 37. public: 38. std::array<templateclassa<t>, uiRankNum> maRanklist; 39. }; 40. 41. class XRankUser 42. { 43. 44. public: 45. 46. XRankUser(); 47. 48. ~XRankUser(); 49. 50. void init(); 51. void ScoreRanksSay(); 52. 53. public: 54. TemplateClassB<sixday_rank_num,long long=""> ScoreRanks; //#####标记1###### 55. }; 56. 57. #endif /* Header_h */ 58. </sixday_rank_num,long></templateclassa<t></unsigned></class></string></array> [cpp] view plain copy 1. // 2. // Header.cpp 3. // Demo 4. // 5. // Created by 杜国超 on 17/2/15. 6. // Copyright © 2017年 杜国超. All rights reserved. 7. // 8. 9. #include "../inc/Header.h" 10. 11. template<class t=""> 12. TemplateClassA<t>::TemplateClassA() 13. { 14. 15. } 16. 17. template<class t=""> 18. void TemplateClassA<t>::init(T Id, int iScore) 19. { 20. mId = Id; 21. miScore = iScore; 22. } 23. 24. template<class t=""> 25. void TemplateClassA<t>::reset() 26. { 27. miScore = 0; 28. } 29. 30. template<unsigned int="" uiranknum,class="" t=""> 31. TemplateClassB<uiranknum,t>::TemplateClassB() 32. { 33. 34. } 35. template<unsigned int="" uiranknum,class="" t=""> 36. TemplateClassB<uiranknum,t>::~TemplateClassB() 37. { 38. reset(); //#####标记1###### 39. } 40. 41. template<unsigned int="" uiranknum,class="" t=""> 42. void TemplateClassB<uiranknum,t>::reset() 43. { 44. printf("TemplateClassB reset \n"); 45. } 46. 47. template<unsigned int="" uiranknum,class="" t=""> 48. void TemplateClassB<uiranknum,t>::say() 49. { 50. printf("TemplateClassB say \n"); 51. } 52. 53. XRankUser::XRankUser() 54. { 55. 56. } 57. 58. XRankUser::~XRankUser() 59. { 60. 61. } 62. 63. void XRankUser::init() 64. { 65. 66. } 67. 68. void XRankUser::ScoreRanksSay() 69. { 70. ScoreRanks.say(); //#####标记2###### 71. } 72. </uiranknum,t></unsigned></uiranknum,t></unsigned></uiranknum,t></unsigned></uiranknum,t></unsigned></t></class></t></class></t></class> [cpp] view plain copy 1. #include <iostream> 2. using namespace std; 3. 4. #include "inc/Header.h" 5. 6. int main(int argc, const char * argv[]) 7. { 8. XRankUser rank; 9. rank.ScoreRanksSay(); //#####标记1###### 10. 11. rank.ScoreRanks.reset(); //#####标记2###### 12. 13. rank.ScoreRanks.say(); //#####标记3###### 14. 15. 16. TemplateClassB<sixday_rank_num,long long=""> ScoreRanks; //#####标记4###### 17. 18. return 0; 19. } 20. </sixday_rank_num,long></iostream>
首先现在的代码结构,模版的声明和定义是分开的,并且是可以编译通过的,但是要注意,如果我们注释header.h文件里的标记1的代码(注意此时用到此成员变量的地方也要相应注释掉),main函数中的标记4处的代码将会报错,打开注释运行正常,因为标记1处的声明导致了在构造函数中调用了相关模版的构造函数,此时构造函数被编译,所以才编译通过。同理如果我们去掉header.cpp中的标记1,那么main函数的标记2会报错,去掉header.cpp的标记2main函数的标记3会报错,当然我们通常会通过main函数标记1代替标记3的代码,这里只是为了说明问题,可见在.cpp文件中被调用过的函数是不受模版的影响的,这也就证明了在相应的.cpp文件中没有被调用的函数没有背编译进.obj文件中。
因此我们在写c++模版的时候两种方式来避免分离式编译对模版的影响,一是把声明和定义放在同一个文件里,二就是在模版所在的.obj文件里调用模版的函数,就像header头文件和cpp文件标记的代码一样。
以上就介绍了C/C+的相关知识,希望对C/C+有兴趣的朋友有所帮助。了解更多内容,请关注职坐标编程语言C/C+频道!
您输入的评论内容中包含违禁敏感词
我知道了
请输入正确的手机号码
请输入正确的验证码
您今天的短信下发次数太多了,明天再试试吧!
我们会在第一时间安排职业规划师联系您!
您也可以联系我们的职业规划师咨询:
版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
沪公网安备 31011502005948号