最近在interpreter项目的编写中遇到了一些工程项目中常见的问题,说来就是一些变量和函数需要在不同的多个文件中使用的问题,为了把这些问题弄清楚,我查阅了关于extern关键字、C++语言编译原理、变量函数声明和定义等相关的问题。
这里准备利用深入浅出的讲解方式。
首先我们需要弄懂在IDE当中,当我们对所建的工程进行build的时候,其实我们的VC2008做了两步工作:
第一步,将每个.cpp(.c)和相应.h文件编译成 obj文件(C+ +语言支持“分别编译”(separate compilation)。也就是说,一个程序所有的内容,可以分成不同的部分分别放在不同的.cpp文件里。.cpp文件里的东西都是相对独立的,在编译(compile)时不需要与其他文件互通,只需要在编译成目标文件后再与其他的目标文件做一次链接(link)就行了。);
第二步,将工程中所有的obj文件进行LINK生成最终的.exe文件。
那么错误就有可能在两个地方产生,一个是编译时的错误,这个主要是语法错误,另一个是连接错误,主要是重复定义变量等。
编译单元:我们所说的编译单元就是指在编译阶段生成的每个obj文件,一个obj文件就是一个编译单元,也就是说一个cpp(.c)和它相应的.h文件共同组成了一个编译单元,一个工程由很多个编译单元组成,每个obj文件里包含了变量存储的相对地址等 。(以下将生成两个obj文件,也就是两个编译单元)
其次,我们需要知道哪些东西应该放在.h文件中,哪些东西应该放在.cpp文件中?
通常情况下头文件中应该只放变量和函数的声明,而不能放它们的定义。因为一个头文件的内容实际上是会被引 入到多个不同的.cpp文件中的,并且它们都会被编译。放声明当然没事,如果放了定义,那么也就相当于在多个文件中出现了对于一个符号(变量或函数)的定 义,这样是不能通过编译的。
不过也有一下几种特殊情况:
一,头文件中可以写const对象的定义。因为全局的const对象默认是没有extern的声明的,所以它只在当前文件中有效。把这样的对象写进头文件中,即使它被包含到其他多个.cpp文件中,这个对象也都只在包含它的那个文件中有效,对其他文件来说是不可见的,所以便不会导致多重定义。
二,头文件中可 以写内联函数(inline)的定义。因为inline函数是需要编译器在遇到它的地方根据它的定义把它内联展开的,而并非是普通函数那样可以先声明再链 接的(内联函数不会链接),所以编译器就需要在编译时看到内联函数的完整定义才行。
三,头文件中可以写类(class)的定义。因为在程序中创建一个类的对象时,编译器只有在这个类的定义完全可见的情况下,才能知道这个类的对象应该如何布局,所以,关于类的定义的要求,跟内联函数是基本一样的。所以把类的定义放进头文件,在使用到这个类的.cpp文件中去包含这个头文件,是一个很好的做法。
那么接下来我们应该弄清楚变量和函数关于声明和定义的区别:
变量的声明有两种情况:
(1) 一种是需要建立存储空间的(定义、声明)。例如:int a在声明的时候就已经建立了存储空间。
(2) 另一种是不需要建立存储空间的(声明)。例如:extern int a其中变量a是在别的文件中定义的。
前者是"定义性声明(defining declaration)"或者称为"定义(definition)",而后者是"引用性声明(referncing declaration)"。从广义的角度来讲声明中包含着定义,但是并非所有的声明都是定义。
变量和函数的声明可以出现多次,但是其定义只能出现一次。
关于extern关键字的作用有两个:
(1) 当extern与“C”连用时,其告诉编译器以下声明的函数在编译时将按照C语言的规则来进行编译,这里主要是避免C++编译方式在编译时为了解决C++中函数重载的问题将函数名弄得面目全非的问题。
(2) 而当extern与变量或者是函数连用时,如extern int global;extern void print( )。表示这个变量或者函数的声明和定义是分开的,本变量或函数在本模块和其他模块中都可以使用。
说了extern关键字,我们就必须提一下static关键字。
用static来声明一个变量的作用有二:
(1) 对于局部变量用static声明,则是为该变量分配的空间在整个程序的执行期内都始终存在。
(2) 外部变量用static来声明,则该变量的作用只限于本文件模块。
有了以上知识,我们在由多个文件所组成的工程项目中,对于将会在多个文件中使用到的变量和函数就可以采用以下方式进行组织:
(1) 在XXX.h文件中用extern声明将在多个文件中用到的全局变量(注意这里仅仅是进行声明,如果对变量赋了值,那么即使有extern也会被编译器视为定义)和函数,然后在相对应的XXX.cpp文件中对已经在XXX.h中声明的函数进行定义(这个cpp文件中需要#include “XXX.h”)。当然,你可以将全局变量的声明和函数的声明分离开(VarXXX.h和FunXXX.h)。#include:#include 是一个来自C语言的宏命令,它在编译器进行编译之前,即在预编译的时候就会起作用。#include的作用是把它后面所写的那个文件的内容,完完整整地、 一字不改地包含到当前的文件中来
(2) 在需要使用已经在XXX.h文件里声明了的全局变量时,我们需要在相应的cpp文件中#include “XXX.h”,然后对相应的全局变量在所有函数外面进行定义(如果这个全局变量已经在其他文件中定义过了,这里只需要用extern进行声明);如果是需要使用已经在XXX.h文件里声明了的函数时(该函数已经在XXX.cpp文件里定义了),我们只需要在相应的cpp文件中#include “XXX.h”,然后即可使用。
下面我们看一个实例:
main.cpp
function.cpp
function.h
程序输出结果:
参考资料:
http://hi.baidu.com/hq81/blog/item/091547ec1c06fb2e62d09f8e.html
http://wrchen.blog.sohu.com/71617539.html
http://hi.baidu.com/zengzhaonong/blog/item/8c10e90366c2bf733912bbb0.html