在C++编程过程当中,随着项目的愈来愈大,代码也会愈来愈多,而且难以管理和分析。因而,在C++中就要分出了头(.h)文件和实现(.cpp)文件,而且也有了Package的概念。html
对于以C起步,C#做为“母语”的我刚开始跟着导师学习C++对这方面仍是感到很模糊。虽然我能够以C的知识面对C++的语法规范,用C#的思想领悟C++中类的使用。可是C#中定义和实现是都在一个文件中(其实都是在类里面),而使用C的时候也只是编程的刚刚起步,所写的程序也只要一个文件就够了。所以对于C++的Package理解以及.h文件和.cpp文件的老是心存纠结。编程
幸亏有详细的PPT让我了解,一次对于Package的认识就明白多了。简单讲,一个Package就是由同名的.h和.cpp文件组成。固然能够少其中任意一个文件:只有.h文件的Package能够是接口或模板(template)的定义;只有.cpp文件的Package能够是一个程序的入口。编辑器
固然更具体详细的讲解,欢迎下载导师的教学PPT-Package来了解更多。ide
不过我在这里想讲的仍是关于.h文件和.cpp文件函数
知道Package只是相对比较宏观的理解:咱们在项目中以Package为编辑对象来扩展和修正咱们的程序。编写代码时具体到应该把什么放到.h文件,又该什么放在.cpp文件中,我又迷惑了。学习
虽然Google给了我不少的连接,可是大部分的解释都太笼统了:申明写在.h文件,定义实现写在.cpp文件。这个解释没有差错,可是真正下手起来,又会发现不知道该把代码往哪里打。spa
因而我又把这个问题抛给了导师,他很耐心地给我详详细细地表述了如何在C++中进行代码分离。很惋惜,第一次我听下了,可是没有听太懂,并且原本对C++就了解不深,因此也没有深入的印象。设计
通过几个项目的试炼和体验以后,我又拿出这个问题问导师,他又一次耐心地给我讲解了一遍(我发誓他绝对不是忘记了我曾经问过一样的问题),此次我把它记录了下来。htm
为了避免再忘记,我将它们总结在这里。对象
非模板类型(none-template) | 模板类型(template) | |
头文件(.h) |
|
|
|
|
|
实现文件(.cpp) |
|
(无) |
|
*申明:declaration
*定义:definition
头文件的全部内容,都必须包含在
#ifndef {Filename}
#define {Filename}
// {Content of head file}
#endif
这样才能保证头文件被多个其余文件引用(include)时,内部的数据不会被屡次定义而形成错误
在头文件中,能够对函数用inline限定符来告知编译器,这段函数很是的简单,能够直接嵌入到调用定义之处。
固然inline的函数并不必定会被编译器做为inline来实现,若是函数过于复杂,编译器也会拒绝inline。
所以简单说来,代码最好短到只有3-5行的才做为inline。有循环,分支,递归的函数都不要用作inline。
对于在类定义内定义实现的函数,编译器自动当作有inline请求(也是不必定inline的)。所以在下边,我把带有inline限定符的函数成员和写在类定义体内的函数成员统称为“要inline的函数成员”
就像前面笼统的话讲的:申明写在.h文件。
对于函数来说,没有实现体的函数,就至关因而申明;而对于数据类型(包括基本类型和自定义类型)来讲,其申明就须要用extern来修饰。
而后在.cpp文件里定义、实现或初始化这些全局函数和全局变量。
不过导师一直反复强调:不准使用全局函数和全局变量。用了以后形成的后果,目前就是交上去的做业项目会扣分。固然不能用自有不能用的理由以及解决方案,不过不在目前的讨论范围内。
对于自定义类型,包括类(class)和结构体(struct),它们的定义都是放在.h文件中。其成员的申明和定义就比较复杂了,不过看上边的表格,仍是比较清晰的。
函数成员不管是否带有static限定符,其申明都放在.h文件的类定义内部。
对于要inline的函数成员其定义放在.h文件;其余函数的实现都放在.cpp文件中。
数据成员的申明与定义都是放在.h文件的类定义内部。对于数据类型,关键问题是其初始化要放在什么地方进行。
对于只含有static限定符的数据成员,它的初始化要放在.cpp文件中。由于它是全部类对象共有的,所以必须对它作合适的初始化。
对于只含有const限定符的数据成员,它的初始化只能在构造函数的初始化列表中完成。由于它是一经初始化就不能从新赋值,所以它也必须进行合适的初始化。
对于既含有static限定符,又含有const限定符的数据成员,它的初始化和定义同时进行。它也是必须进行合适的初始化
对于既没有static限定符,又没有const限定符的数据成员,它的值只针对本对象能够随意修改,所以咱们并不在乎它的初始化何时进行。
C++中,模板是一把开发利器,它与C#,Java的泛型很类似,却又不尽相同。之前,我一直只以为像泛型,模板这种东西我可能一生也不可能须要使用到。可是在导师的强制逼迫使用下,我才真正体会到模板的强大,也真正知道要如何去使用模板,更进一步是如何去设计模板。不过这不是三言两语能够讲完的,就很少说了。
对于模板,最重要的一点,就是在定义它的时候,编译器并不会对它进行编译,由于它没有一个实体可用。
只有模板被具体化(specialization)以后(用在特定的类型上),编译器才会根据具体的类型对模板进行编译。
因此才定义模板的时候,会发现编译器基本不会报错(我当时还很开心的:我写代码尽然会没有错误,一鼓作气),也作不出智能提示。可是当它被具体用在一个类上以后,错误就会大片大片的出现,却每每没法准肯定位。
所以设计模板就有设计模板的一套思路和方式,可是这跟本文的主题也有偏。
由于模板的这种特殊性,它并无本身的准肯定义,所以咱们不能把它放在.cpp文件中,而要把他们所有放在.h文件中进行书写。这也是为了在模板具体化的时候,可以让编译器能够找到模板的全部定义在哪里,以便真正的定义方法。
至于模板类函数成员的定义放在哪里,导师的意见是放在类定义以外,由于这样当你看类的时候,一目了然地知道有那些方法和数据;我在用Visual Studio的时候查看到其标准库的实现,都是放在类内部的。
多是我习惯了C#的风格,我比较喜欢把它们都写在类内部,也由于在开发过程当中,所使用的编辑器都有一个强大的功能:代码折叠。
固然还有其余缘由就是写在类外部,对于每个函数成员的实现都须要把模板类型做为限定符写一遍,把类名限定符也要写一遍。
参考文献:
1. http://www.cnblogs.com/ider/archive/2011/06/30/what_is_in_cpp_header_and_implementation_file.html