设为首页 加入收藏

TOP

C++ 文件include规则 常量定义(一)
2015-07-20 17:57:55 来源: 作者: 【 】 浏览:18
Tags:文件 include 规则 常量 定义

总结一句话就是: C++的函数声明,变量声明,类定义写在头文件里,而函数实现,变量定义,类方法实现写在.cpp文件中;但是对于内联函数和模版类,函数的实现也要写在头文件里!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


1. 将类的成员变量、类方法的定义写在.h中,将类方法的实现写在.cpp中,不要include .cpp文件,不要在.h文件中只写class MyClass; ,一定要写类成员变量和方法的全部定义!!!类方法的实现写在.cpp文件中。

2. 类模版或者模版的定义一定要写在同一个.h中,不要写在.cpp中,不能分开写!!!可以参考 http://blog.csdn.net/ixsea/article/details/6695496 中的解释:对于模板函数来说,只有被调用的模板函数才被实例化,这里的被调用并不要求它必须被main函数调用。某个普通函数调用了模板函数,该模板函数就将对应产生一个实例,而调用它的普通函数可能并不被main调用,也即有可能并不被执行。而普通函数.h 和 .cpp可以分开的原因是已经实例化好的,因此可以根据.h中的定义找到函数实现的位置!!!全文为:

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

观点

包含模型是C++模板源代码的一种组织方式,它鼓励将模板代码全部放在一个.h头文件中,这样可以避免莫名其妙的链接错误。

莫名其妙的链接错误

一般而言,程序员习惯将函数和类的声明放在.h文件、把它们的实现放在.cpp文件,这种多文件组织方式一直被倡导。一方面,这种分离使得代码逻辑清晰,想要了解程序用到哪些全局函数和类,只要查看.h文件就可以。如果把声明和实现都揉在一起,带来的麻烦可想而知,要在一堆乱糟糟的代码中寻找函数名、类名、成员名是一种折磨。另一方面,在构建动态链接库时,这种组织方式是必需的。因为动态链接库是二进制级别上的代码复用,它的一大优点就是具体的实现过程被隐藏起来,全部揉在一个.h文件中显然不符合要求。

然而不幸的是,当程序员仍然按照这种好的习惯编写模板代码时,却出现了问题。比如下面这个简单的例子:

  1. // Bigger.h
  2. template
  3. T Bigger(T,T);
  4. //Bigger.cpp
  5. #include"Bigger.h"
  6. template
  7. T Bigger(T a,T b)
  8. {
  9. return a>b?a:b;
  10. }
  11. //main.cpp
  12. #include"Bigger.h"
  13. #include
  14. using namespace std;
  15. int main()
  16. {
  17. cout< system("pause");
  18. return 0;
  19. }

    这几行代码很简单,分成了三个文件Bigger.h、Bigger.cpp以及main.cpp,分别对应模板函数Bigger的声明、定义和使用。看起来结构清晰,符合好的编码习惯,编译链接却得到这样的错误提示:

    Error 1 error LNK2019: unresolved external symbol "int __cdecl Bigger (int,int)" (??$Bigger@H@@YAHHH@Z) referenced in function _main E:\Codes\Chapter3Lab\includeModel\main.obj
    意思是链接器找不到main.obj里Bigge 函数的实现。这种看起来毫无道理的链接错误,也很好的体现了模板的实例化规则。

    模板的实例化规则

    对于模板函数来说,只有被调用的模板函数才被实例化,这里的被调用并不要求它必须被main函数调用。某个普通函数调用了模板函数,该模板函数就将对应产生一个实例,而调用它的普通函数可能并不被main调用,也即有可能并不被执行。
    模板类也有类型的实例化规则,特别的是即使显式实例化了类模板,类模板的成员函数也未必被实例化,这是模板类的“不完全”实例化规则,读者可以点击这里了解更多。

    链接错误的解释

    了解了模板的实例化规则,就可以对上面的链接错误做出解释了。main.cpp中调用了Bigger(10,20),按理说这将引起模板函数Bigger(T,T)被实例化为普通函数,然而在main.cpp所属的翻译单元里并没有Bigger(T,T)的实现,对main.cpp所属的翻译单元来说,Bigger(T,T)的实现是不可见的。因此,由main.cpp所属翻译单元编译得到main.obj时,编译器假设Bigger (int,int)在其它翻译单元中。

    Bigger.cpp虽然有Bigger(T,T)的实现,但是由于在Bigger.cpp所属翻译单元中Bigger并没有被调用,因此Bigger.cpp就没有义务对模板函数Bigger(T,T)进行实例化,于是由它产生的Bigger.obj中也找不到的Bigger (int,int)。

    本文前述例子中的链接错误信息正是表达的这个意思。

    链接错误的进一步探讨

    既然是因为Bigger.cpp没有义务对Bigger(T,T)进行实例化,那么在Bigger.cpp中增加一个调用Bigger (int,int)函数的普通函数是否就可以了呢?在Bigger.cpp文件中添几行代码,如下所示:

    1. //Bigger.cpp
    2. #include"Bigger.h"
    3. template
    4. T Bigger(T a,T b)
    5. {
    6. return a>b?a:b;
    7. }
    8. void g() //增加一个调用Bigger (int,int)的普通函数g()
    9. {
    10. Bigger(1,2);
    11. }

      编译、链接成功,允许结果正确,进一步验证了上述观点。

      解决方法 - 包含模型

      本文列出的例子很简单,规模小,所以按照模板的实例化规则,“人为”地介入到模板函数的实例化过程中并让程序成功运行。但是,在规模较大的程序里,想要人为介入加以控制几乎是不可能的,应该使用C++推荐的包含模型。

      具体做法并不复杂:把模板的声明和定义放在一个.h文件中,凡是用到该模板的.cpp文件包含它所在的.h文件就可以了。上面的例子使用包含模型改写,最终是代码是这样的:

      1. // Bigger.h
      2. template
      3. T Bigger(T a,T b)
      4. {
      5. return a>b?a:b;
      6. }
      7. //main.cpp
      8. #include"Bigger.h"
      9. #include
      10. using namespace std;
      11. int main()
      12. {
      13. cout< system("pause");
      14. return 0;
      15. }

        不过仍然有一个问题值得思考:当多个.cpp文件同时包含Bigger.h时,就有可能产生多份相同类型的实例化,这样是否会造成最终生成的.exe文件变得庞大?这个问题理论上是存在的,不过现在大多数编译器都对此作了一定的优化,一个模板的相同类型有多份实例化体时,编译器最终只保留一个,这样就避免了“代码膨胀”的问题。


        下面给一个例子:

        //main.cpp

        #include "person.h"
        #include "SmartPointe
首页 上一页 1 2 3 4 下一页 尾页 1/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇HDU2566 统计硬币 下一篇uva 1378 - A Funny Stone Game(..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: