资源预览内容
第1页 / 共18页
第2页 / 共18页
第3页 / 共18页
第4页 / 共18页
第5页 / 共18页
第6页 / 共18页
第7页 / 共18页
第8页 / 共18页
第9页 / 共18页
第10页 / 共18页
亲,该文档总共18页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
C+多文件程序多文件结构和编译预处理命令一.C+ 程序的多文件结构一个程序按结构至少可以划分为三个文件:类的声明文件(*.h 文件)、类的实现文件(*.cpp 文件)和主函数文件(使用到类的文件,*.cpp 文件),如果程序更复杂,我们会为每个类单独建一个声明文件和一个实现文件。这样我们要修改某个类时就直接找到它的文件修改即可,不需要其他的文件改动。时钟类的例子:/ 文件 1:Clock 类的声明,可以起名为 Clock.h#include using namespace std;class Clock /时钟类声明public: /外部接口Clock();void SetTime(int NewH, int NewM, int NewS); /三个形参均具有函数原型作用域void ShowTime();Clock() private: /私有数据成员int Hour,Minute,Second;/ 文件 2:Clock 类的实现,可以起名为 Clock.cpp#include Clock.h/时钟类成员函数实现Clock:Clock() /构造函数Hour=0;Minute=0;Second=0;void Clock:SetTime(int NewH,int NewM,int NewS)Hour=NewH;Minute=NewM;Second=NewS;void Clock:ShowTime()coutAdd Class,在弹出的对话框中选择 c+ class,然后由弹出个对话框,在 class name 处填上类名点 finish 就可以了,这样.h 文件和.cpp 文件会自动生成,我们也可以点 Project-Add New Item,在弹出的对话框中选择 Header File(.h)或 C+ File(.cpp)来生成.h 文件或.cpp 文件。二.编译预处理程序编译器在编译源程序以前,要由预处理程序对源程序文件进行预处理。预处理程序提供了一些编译预处理指令和预处理操作符。预处理指令都要由“#”开头,每个预处理指令必须单独占一行,而且不能用分号结束,可以出现在程序文件中的任何位置。1.#include 指令#include 指令也叫文件包含指令,用来将另一个源文件的内容嵌入到当前源文件该点处。其实我们一般就用此指令来包含头文件。#include 指令有两种写法:#include 使用这种写法时,会在 C+安装目录的 include 子目录下寻找 中标明的文件,通常叫做按标准方式搜索。#include 文件名使用这种写法时,会先在当前目录也就是当前工程的目录中寻找中标明的文件,若没有找到,则按标准方式搜索。2.#define 和 #undef 指令如果你学过 C 语言,就会知道用#define 可以定义符号常量,比如,#define PI 3.14 这条指令定义了一个符号常量 PI,它的值是 3.14。C+也可以这样定义符号常量,但一般更常用的是在声明时用 const 关键字修饰。C 语言还用#define 定义参数宏,来实现简单的函数运算,比如,#define add(x,y) (x+y) 这条指令说明如果我们用到 add(1,2)则预处理后就会用(1+2)代替,C+中一般用内联函数来实现。#undef 用来删除由#define 定义的宏,使其不再起作用。3.条件编译指令用条件编译指令可以实现某些代码在满足一定条件时才会参与编译,这样我们可以利用条件编译指令将同一个程序在不同的编译条件下生成不同的目标代码。例如,我们可以在调试程序时加入一些调试语句,用条件编译指令控制只有在debug 模式下这些调试语句才参与编译,而在 release 模式下不参与编译。条件编译指令有 5 中形式:a.第一种形式:#if 常量表达式 程序正文 /当“ 常量表达式 ”非零时本程序段参与编译#endifb.第二种形式:#if 常量表达式程序正文 1/当“ 常量表达式 ”非零时本程序段参与编译#else程序正文 2/当“ 常量表达式 ”为零时本程序段参与编译#endifc.第三种形式:#if 常量表达式 1程序正文 1 /当“ 常量表达式 1”非零时本程序段参与编译elif 常量表达式 2程序正文 2 /当“常量表达式 1”为零、“ 常量表达式 2”非零时本程序段参与编译.elif 常量表达式 n程序正文 n /当“常量表达式 1”、.、“常量表达式 n-1”均为零、“ 常量表达式 n”非零时本程序段参与编译#else程序正文 n+1 /其他情况下本程序段参与编译#endifd.第四种形式:#ifdef 标识符程序段 1#else程序段 2#endif如果“标识符”经#defined 定义过,且未经 undef 删除,则编译程序段 1,否则编译程序段 2。e.第五种形式:#ifndef 标识符程序段 1#else程序段 2#endif如果“标识符”未被定义过,则编译程序段 1,否则编译程序段 2。4.define 操作符define 是预处理操作符,不是指令,所以不能用#开头。使用形式为:define(标识符)。如果括号里的标识符用#define 定义过,并且没有用#undef删除,则 define(标识符)为非 0,否则为 0。可以这样使用:#if !define(HEAD_H)#define HEAD_H我们在包含头文件时,有时多次重复包含同一个头文件,比如下面这种情况:/ main.cpp 文件#include file1.h#include file2.hint main()/ file1.h 文件#include head.h/ file2.h 文件#include head.h/ head.h 文件.class A.main.cpp 包含了 file1.h 文件,file1.h 又包含了 head.h 文件,main.cpp 还包含了 file2.h 文件,file2.h 也包含了 head.h 文件,那么 main.cpp 就包含了两次 head.h 文件,在编译时就会报错,说 head.h 中的类 A 重复定义了。这时我们可以在被重复包含的文件 head.h 中使用条件编译指令,用一个唯一的标识符来标识 head.h 文件是否已经编译过,如果已经编译过则不会重复编译了。鸡啄米给大家改写下上面的 head.h 文件:/ head.h 文件#ifndef HEAD_H#define HEAD_H.class A.#endif在这个改好的 head.h 文件中,上来会先判断 HEAD_H 是否被定义过,如果没有被定义过,则 head.h 文件还没参与过编译,就编译此文件中的源代码,同时定义 HEAD_H,标记 head.h 文件已经参与过编译。如果 HEAD_H 已经被定义过,则说明此文件已经参与过编译,编译器会跳过本文件左右内容编译其他部分,类 A 也不会有重复定义的错误了。-C 语言的要求:函数先声明后定义-example 1: test1.c 中#includeint main(void) printf(%dn,fun(5);int fun()printf(Hello Worldn);/代码编译,链接都能够通过。但是会有警告:warning C4013: fun undefined; assuming extern returning intwarning C4716: fun : must return a value通过可以看出虽然 fun(5)在使用前没有声明,但是编译器仅仅是给出警告,并提供默认的函数声明类型:int name(void); (参考 C 语言创始人的书) ,编译器在遇到 fun(5) 的“fun(”的时候就假设 fun 是一个函数,如果没有找到 fun 函数,就提供默认的函数声明,且忽略 fun 中的函数参数。并在链接的过程中找到了 fun 函数的定义,则通过链接,生成可执行代码。如下代码,能够通过编译,却不能通过链接,因为链接的时候找不到 fun 函数的定义。#includeint main(void)printf(%dn,fun(5);-注:第一个程序在 C+编译器里不能通过编译,因为 C+编译器严格要求函数先声明后使用,如果找不到声明,不能通过编译,编译器并不会提供默认的函数声明。-多文件组织中函数的先声明后定义-test1.c#includeint main(void)printf(%dn,fun(5);-test2.cint fun()printf(Hello Worldn);-上述文件在编译时能够通过编译,并且能偶通过链接,但是会产生如下警告:Test1.c : warning C4013: fun undefined; assuming extern returning intTest2.c:warning C4013: printf undefined; assuming extern returning intwarning C4716: fun : must return a valuetest1.c 中产生警告的原因是上面已经解释过了,链接能够通过是链接器能够自动的把一起编译的 test1.c 和 test2.c 中可执行代码链接起来,这样 test1.c 中的 fun 函数就找到了定义。Test2.c 中第一个警告是由于上述同样的原因,能够通过链接是由于 test1.c 中已经包含了头文件 ,把 printf 的可执行代码给链接过来了,所以 test2.c 中的 printf 也就能够找到定义。-上述例子说明多个源文件文件在编译环境下可以通过链接器自动连接到一起并不需要头文件的参与,但是为了程序的可移植性和正确性,一定要遵循先声明后使用的原则:这个原则不但适用于函数,也使用全局变量。-头文件中应该包含什么-test.hint a;void show(void)printf(Hello worldn);-test1.cint fun()printf(Hello Worldn);-test2.c#include#include test.hint main(void)printf(%dn,fun(5);上述编译和链接同样能够通过,通过上面的例子不难理解:因为就是在头文件中定义了全局变量,并把它包含在 test2.c 中;-test.hint a;void show(void)printf(Hello worldn);-test1.c#include “test.h ”int fun()printf(Hello Worldn);-test2.c#include#include test.hint main(void)printf(%dn,fun(5);-上述多文件程序能够通过编译,却不能通过链接:error LNK2005: _show already defined in test1.objfatal error LNK1169: one or more multiply defined symbols found具体原因有以下两个:1:test1.c 包含了 test.h 的头文件,定义了全局变量 a,同理 test.2 也通过相同的手段定义了全局变量 a;2: test1.c 和 test2.c 通过上述手段分别提供了 show 的定义-上述分析指出在链
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号