C++程序的多文件结构

给大家看了很多比较完整的C++程序的例子,大家可能发现了,它们的结构基本上可以分为三个部分:类的声明、类的成员函数的实现和主函数。因为代码比较少,所以可以把它们写在一个文件中,但是我们实际进行软件开发时,程序会比较复杂,代码量比较大。

一个程序按结构至少可以划分为三个文件:类的声明文件(*.h文件)、类的实现文件(*.cpp文件)和主函数文件(使用到类的文件),如果程序更复杂,我们会为每个类单独建一个声明文件和一个实现文件。这样我们要修改某个类时就直接找到它的文件修改即可,不需要其他的文件改动。

有个时钟类的例子,现在给大家看下将那个程序按照上面说的结构分到三个文件里:

// 文件1:Clock类的声明,可以起名为Clock.h
#include <iostream>
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()
{cout<<Hour<<":"<<Minute<<":"<<Second<<endl;
}
// 文件3:主函数,可以起名为main.cpp
#include "Clock.h"
//声明全局对象g_Clock,具有文件作用域,静态生存期
Clock g_Clock;
int main() //主函数
{cout<<"文件作用域的时钟类对象:"<<endl;//引用具有文件作用域的对象:g_Clock.ShowTime();g_Clock.SetTime(10,20,30);Clock myClock(g_Clock);    //声明具有块作用域的对象myClock,并通过默认拷贝构造函数用g_Clock初始化myClockcout<<"块作用域的时钟类对象:"<<endl;myClock.ShowTime(); //引用具有块作用域的对象return 0;
}


Clock.cpp和main.cpp都使用#include "Clock.h"把类Clock的头文件Clock.h包含进来。

#include指令的作用就是将#include后面的文件嵌入到当前源文件该点处,被嵌入的文件可以是.h文件也可以是.cpp文件。

如果不包含Clock.h,Clock.cpp和main.cpp就不知道Clock类的声明形式,就无法使用此类,所以所有使用此类的文件都应该包含声明它的头文件。

上面的程序在编译时,由Clock.cpp和Clock.h编译生成Clock.obj,由main.cpp和Clock.h编译生成main.obj,然后就是链接过程,Clock.obj和main.obj链接生成main.exe可执行文件。

如果我们只修改了类的实现文件,那么只需重新编译Clock.cpp并链接就可以,别的文件不用管,这样就提高了效率。在Windows系统中的C++程序用工程来管理多文件结构,而Unix系统一般用make工具管理,如果大家从事Unix系统软件开发,就需要自己写make文件。

编译预处理程序

编译器在编译源程序以前,要由预处理程序对源程序文件进行预处理。

预处理程序提供了一些编译预处理指令和预处理操作符。预处理指令都要由“#”开头,每个预处理指令必须单独占一行,而且不能用分号结束,可以出现在程序文件中的任何位置。

#include指令
#include指令也叫文件包含指令,用来将另一个源文件的内容嵌入到当前源文件该点处。其实我们一般就用此指令来包含头文件。

#include指令有两种写法:

#include <文件名>

使用这种写法时,会在C++安装目录的include子目录下寻找<>中标明的文件,通常叫做按标准方式搜索。

 #include "文件名"

使用这种写法时,会先在当前目录也就是当前工程的目录中寻找""中标明的文件,若没有找到,则按标准方式搜索。

#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定义的宏,使其不再起作用。

条件编译指令
用条件编译指令可以实现某些代码在满足一定条件时才会参与编译,这样我们可以利用条件编译指令将同一个程序在不同的编译条件下生成不同的目标代码。

例如,我们可以在调试程序时加入一些调试语句,用条件编译指令控制只有在debug模式下这些调试语句才参与编译,而在release模式下不参与编译。

条件编译指令有5中形式:

a.第一种形式:

#if  常量表达式         程序正文    //当“ 常量表达式”非零时本程序段参与编译
#endif

b.第二种形式:

#if   常量表达式程序正文1      //当“ 常量表达式”非零时本程序段参与编译
#else程序正文2      //当“ 常量表达式”为零时本程序段参与编译
#endif

c.第三种形式:

#if 常量表达式1程序正文1     //当“ 常量表达式1”非零时本程序段参与编译
elif 常量表达式2程序正文2    //当“常量表达式1”为零、“ 常量表达式2”非零时本程序段参与编译
...
elif 常量表达式n程序正文n    //当“常量表达式1”、...、“常量表达式n-1”均为零、“ 常量表达式n”非零时本程序段参与编译
#else程序正文n+1   //其他情况下本程序段参与编译
#endif

d.第四种形式:

#ifdef 标识符程序段1
#else程序段2
#endif

如果“标识符”经#defined定义过,且未经undef删除,则编译程序段1,否则编译程序段2。

e.第五种形式:

#ifndef 标识符程序段1
#else程序段2
#endif

如果“标识符”未被定义过,则编译程序段1,否则编译程序段2。

define操作符
define是预处理操作符,不是指令,所以不能用#开头。使用形式为:define(标识符)。如果括号里的标识符用#define定义过,并且没有用#undef删除,则define(标识符)为非0,否则为0。可以这样使用:

#if !define(HEAD_H)
#define HEAD_H

我们在包含头文件时,有时多次重复包含同一个头文件,比如下面这种情况:

// main.cpp文件
#include "file1.h"
#include "file2.h"
int 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文件是否已经编译过,如果已经编译过则不会重复编译了。

多文件结构和编译预处理命令相关推荐

  1. C语言中编译预处理命令作用,C语言预处理命令详解

    原标题:C语言预处理命令详解 关注百问科技并将它设为星标 不错过任何一篇嵌入式干货 ------ 作者:clover_toeic 原文出处: https://www.cnblogs.com/clove ...

  2. C语言中 编译预处理命令的作用有哪些,C语言系列——预处理命令

    是什么? 首先介绍一下什么是预处理,在编译之前对源文件进行简单加工的过程,就称之为预处理.又因为预处理主要是处理#开头的命令,故将以#号开头的命令称为预处理命令. 做什么? 今天我们主要讨论C语言中的 ...

  3. 0与1c语言编译,C语言程序设计(07776-1)第11章编译预处理课案.ppt

    C语言程序设计(07776-1)第11章编译预处理课案.ppt 第11章 编译预处理 主要内容 宏定义 文件包含 条件编译 程序案例 小结 习题 11-1 宏定义 不带参数的宏定义 带参数的宏定义 终 ...

  4. 2.c语言编译预处理,c语言第03章-编译预处理2.ppt

    c语言第03章-编译预处理2 第3章 编译预处理 编译预处理是指,编译时,首先对编译预处理命令进行处理,然后再将预处理后的中间结果进行编译,以得到目标代码. 教学目的: 掌握#define.#incl ...

  5. c语言里有js的预编译环节吗,C语言第十一讲,预处理命令.

    C语言第十一讲,预处理命令. 一丶预处理简介 什么是预处理,预处理就是预先处理. 我们知道,程序会经过编译,连接形成可执行文件 这些在编译之前对源文件进行简单加工的过程,就称为预处理(即预先处理.提前 ...

  6. 宏定义处理特殊字符 -_c语言编译与预处理命令

    所谓预处理,是指源文件在进行编译的第一遍扫描之前所作的工作,由预处理程序完成.当对一个源文件进行编译时,系统将自动引用预处理程序对源程序中的预处理部分进行处理,处理完毕自动进入对源程序的编译. 在前面 ...

  7. c语言预处理命令12个,C语言编译预处理和预处理命令

    1.预处理概念:编译程序时,编译器将对程序文件作至少两个阶段的编译预处理,预处理程序预处理阶段和编译程序预处理阶段. 预处理程序预处理阶段:C预处理程序先于C编译程序运行.预处理程序从前向后根据预处理 ...

  8. vc++学习篇(三)——预处理命令之条件编译(#ifdef,#else,#endif,#if等)

    预处理就是在进行编译的第一遍词法扫描和语法分析之前所作的工作.说白了,就是对源文件进行编译前,先对预处理部分进行处理,然后对处理后的代码进行编译.这样做的好处是,经过处理后的代码,将会变的很精短.   ...

  9. [转]详解编译预处理

    预处理  预处理语句是一些行首以#开始的特殊语句,例如:#include,#define等就是预处理语句.在编译程序的编译过程中,进行其它编译处理(词法分析.语法分析.代码生成.优化和连接等)之前,先 ...

最新文章

  1. 【WPF】拖拽ListBox中的Item
  2. python创建和控制的实体称为_Python语法基础
  3. html的表单图形验证码怎么做,django中简单图形验证码实现
  4. 在Xuper链上部署Java语言智能合约和分析存证合约的实现逻辑
  5. 冯鑫涉嫌犯罪被带走 暴风集团开盘即跌停
  6. 演示FilterConfig接口的getInitParameter(String name)方法
  7. 【shell】Shell编程的前篇以及通过编程实现,jdk的安装
  8. python网络安全协议_python网络安全
  9. Context mContext = getApplicationContext();
  10. 存储控制器和SDRAM 实验
  11. dw中创建java程序_新建MainGame.java并创建窗口
  12. 北斗导航 | RAIM算法流程图
  13. CANAPE字体颜色c语言,CANape介绍CANape介绍.doc
  14. java 输入人名输出_Java 输入汉字姓名 输出 姓名拼音 首字母缩写组合
  15. Vue2源码学习笔记 - 12.响应式原理—Dep 类详解
  16. video标签使用controls属性怎么去掉三个点
  17. A Question of Ingestion(Dp)
  18. hadoop详细笔记(十一) mapreduce数据分析案例之线段重叠案例
  19. Eclipse启动加载工作空间突然闪退
  20. Python:whl文件是神?如何安装whl文件?

热门文章

  1. 计算机系统-电路设计07-上升沿D触发器的内部电路实现/移位寄存器/串行接口/并行接口
  2. python random模块中的指令_python中random模块的使用
  3. 算数运算符与关系运算符_Swift进阶三——运算符相关
  4. python库下载本地安装_Python包方法的本地安装,python
  5. c语言屏蔽按键,VC实现让关闭按钮成灰色不可用的方法
  6. 暑假集训 || 网络流
  7. Java实例---flappy-bird实例[最终版]
  8. PHP递归删除目录及目录下的文件
  9. BZOJ——3343: 教主的魔法 || 洛谷—— P2801 教主的魔法
  10. Uva 10817 校长的烦恼