一.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初始化myClock
                  cout<<"块作用域的时钟类对象:"<<endl; 
                  myClock.ShowTime(); //引用具有块作用域的对象
                  return 0;
       }

在vs2010中如何生成这三个文件呢?我们可以点菜单中Project->Add Class,在弹出的对话框中选择c++ class,然后由弹出个对话框,在class name处填上类名点finish就可以了,这样.h文件和.cpp文件会自动生成,我们也可以点Project->Add New Item,在弹出的对话框中选择Header File(.h)或C++ File(.cpp)来生成.h文件或.cpp文件。

Clock.cpp和main.cpp都使用#include "Clock.h"把类Clock的头文件Clock.h包含进来。#include指令的作用就是将#include后面的文件嵌入到当前源文件该点处,被嵌入的文件可以是.h文件也可以是.cpp文件。如果不包含Clock.h,Clock.cpp和main.cpp就不知道Clock类的声明形式,就无法使用此类,所以所有使用此类的文件都应该包含声明它的头文件。关于#include指令下面鸡啄米会讲。

上面的程序在编译时,由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文件。

二.编译预处理程序

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

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  常量表达式         
                程序正文        //当“ 常量表达式”非零时本程序段参与编译
       #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。

4.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文件是否已经编译过,如果已经编译过则不会重复编译了。鸡啄米给大家改写下上面的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也不会有重复定义的错误了。

鸡啄米今天给大家讲的内容比较多,大家慢慢看吧,其实并不难。编程入门学习中难免有问题,如果有什么问题大家可以在鸡啄米博客上留言讨论,谢谢大家

转载请标明本文地址:http://www.jizhuomi.com/software/70.html

C++程序设计必知:多文件结构和编译预处理命令相关推荐

  1. 多文件结构和编译预处理命令

    C++程序的多文件结构 给大家看了很多比较完整的C++程序的例子,大家可能发现了,它们的结构基本上可以分为三个部分:类的声明.类的成员函数的实现和主函数.因为代码比较少,所以可以把它们写在一个文件中, ...

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

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

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

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

  4. c语言类静态数据成员函数,鸡啄米:C++编程入门系列之二十一(C++程序设计必知:类的静态成员)...

    鸡啄米在上一讲数据和函数中讲到,函数之间共享数据也就是此函数访问彼函数的数据主要是通过局部变量.全局变量.类的数据成员.类的静态成员及友元实现的,前三个已经讲过了,这一讲鸡啄米来讲讲静态成员.静态成员 ...

  5. web程序设计-必知的css基础你还不知道?04

    CSS的引入为指定HTML文档的呈现细节提供了一种既 统一又一致的方法,样式表分为三个层次:行内样式表. 文档层样式表和外部样式表.优先级是行内样式表大于文 档样式表,文档样式表大于外部样式表. 样式 ...

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

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

  7. 【C语言】编译预处理和宏(附带##介绍)

    参考中国大学MOOC 浙江大学翁恺C语言程序设计在线课程 1.什么是编译预处理指令 #开头的是编译预处理指令 它们不是C语⾔的成分,但是C语⾔程序离不开它们 #define⽤来定义⼀个宏 2.用#de ...

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

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

  9. 【程序设计】XML必知必会

    文章目录 XML XML语法规则纲要 XML文档必须有且只有一个根元素 XML元素都必须有一个关闭标签 XML标签对大小写敏感 XML元素必须被正确的嵌套 XML属性值必须加引号 Demo-XML文档 ...

  10. SQL Server必知必会

    SQL Server必知必会 2009-10-27-17:57:57 Structure     Query     Language:SQL 结构化       查询      语言 数据库产品: ...

最新文章

  1. 计算机接口实验1,计算机接口技术实验一.doc
  2. win7磁盘清理_window7越来越卡?系统残余文件太多,磁盘清理可以搞定!!
  3. proxifier访问https错误_教你实现IE访问https网站不出错方法
  4. openpyxl 绘制饼形图_好享学丨快速上手Pythonmatplotlib 箱线图绘制,学术人必备
  5. Java Web学习总结(18)——JSP标签
  6. python3 django html 中文乱码_解决django后台管理界面添加中文内容乱码问题
  7. Java读取URL到字符串
  8. JVM性能优化, Part 5:Java的伸缩性
  9. 我拿什么拯救你,混乱的思维?不如试试这3款神器
  10. WinRAR命令行参数
  11. python md5解密_Python md5解密
  12. Microsoft FxCop 的设计规则 .
  13. 驻点运维人员被客户投诉要求换人,换还是不换?
  14. JS 时间函数实现9宫格抽奖
  15. 调用百度API(二)——百度翻译
  16. 基于MATLAB的图片中文字的提取及识别
  17. c语言程序冒号的作用是什么,C语言里面的冒号
  18. 创建deployment
  19. 只需两步,教你正确识别百度蜘蛛
  20. 设计一个三维向量类,并实现向量的加法、减法以及向量与标量的乘法和除法运算。

热门文章

  1. 数据结构最短路径例题_数据结构(五)图---最短路径(迪杰斯特拉算法)
  2. ollvm源码分析之控制流扁平化(3)
  3. IDEA+Maven搭建JavaWeb项目
  4. 将指定目录中的txt文件转化成excel文件
  5. 第三百一十八节,Django框架,信号
  6. java使用RunTime调用windows命令行
  7. easyui关于validatebox实现多重规则验证的实践
  8. Jsp Layout 布局页
  9. linux phpredisAdmin安装步骤
  10. 【Andrioid】在Gradle编译时生成一个不同的版本号,动态设置应用程序标题,应用程序图标,更换常数...