当前课程知识点:C++语言程序设计基础 >  第5章 数据的共享与保护 >  多文件结构和预编译命令 >  多文件结构和预编译命令(例5-10)

返回《C++语言程序设计基础》慕课在线视频课程列表

多文件结构和预编译命令(例5-10)在线视频

多文件结构和预编译命令(例5-10)

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

C++程序的一般组织结构

l 一个工程可以划分为多个源文件:

n    类声明文件(.h文件)

n    类实现文件(.cpp文件)

n    类的使用文件(main()所在的.cpp文件)

l 利用工程来组合各个文件。

例 5-10 多文件的工程

//文件1,类的定义,Point.h

class Point { //类的定义

public:          //外部接口

       Point(int x = 0, int y = 0) : x(x), y(y) { }

       Point(const Point &p);

       ~Point() { count--; }

       int getX() const { return x; }

       int getY() const { return y; }

       static void showCount();          //静态函数成员

private:         //私有数据成员

       int x, y;

       static int count; //静态数据成员

};

 

//文件2类的实现,Point.cpp

#include "Point.h"

#include <iostream>

using namespace std;

 

int Point::count = 0;            //使用类名初始化静态数据成员

 

Point::Point(const Point &p) : x(p.x), y(p.y) {

       count++;

}

 

void Point::showCount() {

       cout << "  Object count = " << count << endl;

}

 

//文件3主函数5_10.cpp

#include "Point.h"

#include <iostream>

using namespace std;

 

int main() {

       Point a(4, 5);      //定义对象a其构造函数使count1

       cout <<"Point A: "<<a.getX()<<", "<<a.getY();

       Point::showCount();      //输出对象个数

       Point b(a);         //定义对象b其构造函数回使count1

       cout <<"Point B: "<<b.getX()<<", "<<b.getY();

       Point::showCount();      //输出对象个数

       return 0;

}

外部变量

l 如果一个变量除了在定义它的源文件中可以使用外,还能被其它文件使用,那么就称这个变量是外部变量。

l 文件作用域中定义的变量,默认情况下都是外部变量,但在其它文件中如果需要使用这一变量,需要用extern关键字加以声明。

外部函数

l 在所有类之外声明的函数(也就是非成员函数),都是具有文件作用域的。

l 这样的函数都可以在不同的编译单元中被调用,只要在调用之前进行引用性声明(即声明函数原型)即可。也可以在声明函数原型或定义函数时用extern修饰,其效果与不加修饰的默认状态是一样的。

将变量和函数限制在编译单元内

l 使用匿名的命名空间:在匿名命名空间中定义的变量和函数,都不会暴露给其它的编译单元。

   namespace {         //匿名的命名空间

         int n;

         void f() {

                     n++;

         }

   }

l 这里被“namespace { …… }”括起的区域都属于匿名的命名空间。

标准C++库

l 标准C++类库是一个极为灵活并可扩展的可重用软件模块的集合。标准C++类与组件在逻辑上分为6种类型:

n    输入/输出类

n    容器类与抽象数据类型

n    存储管理类

n    算法

n    错误处理

n    运行环境支持

编译预处理

l #include 包含指令

n    将一个源文件嵌入到当前源文件中该点处。

n    #include<文件名> 

  按标准方式搜索,文件位于C++系统目录的include子目录下

n    #include"文件名"

  首先在当前目录中搜索,若没有,再按标准方式搜索。

l #define 宏定义指令

n    定义符号常量,很多情况下已被const定义语句取代。

n    定义带参数宏,已被内联函数取代。

l #undef

n    删除由#define定义的宏,使之不再起作用。

条件编译指令——#if 和 #endif

#if  常量表达式

 //当“ 常量表达式”非零时编译

     程序正文 

#endif

......

条件编译指令——#else

  #if   常量表达式

            程序正文1   //当“ 常量表达式”非零时编译

#else

     程序正文2   //当“ 常量表达式”为零时编译

#endif

条件编译指令——#elif

#if 常量表达式1

    程序正文1  //当“ 常量表达式1”非零时编译

#elif 常量表达式2

    程序正文2  //当“ 常量表达式2”非零时编译

#else

    程序正文3  //其他情况下编译

#endif

条件编译指令


#ifdef 标识符

    程序段1

#else

    程序段2

#endif

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

l 否则编译程序段2。


#ifndef 标识符

   程序段1

#else

   程序段2

#endif

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

l 否则编译程序段2。

 


下一节:小结

返回《C++语言程序设计基础》慕课在线视频列表

多文件结构和预编译命令(例5-10)课程教案、知识点、字幕

大家好

欢迎继续回来

学习C++语言程序设计

这一节我们来学习

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

我们在写程序的时候呢

如果需要分工合作

如果程序的规模略微大一些

我们就会需要把程序

分别放在不同的文件中

可以分别编译

然后从而放在一起连接

接下来我们就来看

在C++程序中

可以把文件怎样划分成

多文件结构

那C++程序的一般组织结构

是这样的

一个工程文件可以划分为

多个源文件

比如说我们可以将类的声明

放在头文件中

就是后缀.h的文件

可以把类成员函数的实现

放在一个

或多个独立的C++文件中

那使用类的人

和写这个类的代码的人

可能不是一个人

也可能就不是一个团队的人

那么这种情况下

使用类的文件

和类的定义以及实现文件

往往是分开的

所以我们可以模拟这种情况

将主函数所在的文件

放在另一个

独立的C++文件中

然后我们利用工程

来组合各个文件

下面我们就通过一个例子

来看一下

如何将多个文件

组织在一个C++的工程中

现在呢

我们将以前演示过的这个例题

拿来来演示多文件工程

是怎么样组织文件

以及它编译和连接的时候

都完成了一些什么任务

原来我们用过的这个点类的例子

在这儿我们把它分割成

多个文件了

不是把全部代码

都放在一个文件里面

当我们要做

稍微规模大一点的应用的时候

肯定在一个工程

一个project里面要有多个文件的

不会把所有的东西

都放在一个C++文件中

比如这个例子呢

我们就将类的定义

放在一个头文件里面

这是Point类的定义

放在Point.h文件中

系统这个类库里面

提供的这些头文件

都是不带后缀h的

而我们自己写的头文件

通常是带后缀h的

那看这是类的成员函数

实现的文件

在Point.cpp里面

实现这个类的成员函数

以及要初始化的

类的数据静态成员这些

注意这儿有一个包含

include Point.h

我们自己写的这些头文件

就要用双引号引起来

而系统类库里面的头文件

用尖括号括起来

用尖括号括起来的这样的头文件

它会直接到你

安装的时候的默认目录下

去找这个文件

而你用双引号引起来的呢

会首先在当前的工作目录下

去找这个文件

如果找不到

才会到系统约定的目录下去找

所以我们自己定义的头文件

一般是用双引号引起来

而且后缀是有h

那接下来呢再看

这是主函数所在的文件

也就是刚才我们声明了类

而定义了类的成员函数

接下来要使用类了

类的定义者和使用者

在很多情况下

可能不是一个人

甚至于不是一个团队的人

那么这些程序

它很可能又存在于不同的文件中

如果我们要使用呢

可以把它都集中到一个工程

一个project里面来

但是代码不在同一个文件中

现在在主函数中

我们要使用Point类

当然也得将Point类的定义

Point.h作为头文件包含进来

不然的话

编译器在编译的时候

它就不知道Point这个符号

是什么了

好 这三个文件分别编辑好了

接下来我们看一下

整个编译和连接的过程

是什么样的

我们来看这个示意图

Point.h是这个定义

Point类的这个头文件

然后分别在类的实现文件

Point.cpp里面

还有主函数所在的文件

我们起名叫5-10.cpp

分别在这两个文件里面

都包含了Point.h

然后这两个cpp文件

分别进行编译

它们分别进行编译

编译以后各自形成

后缀为obj的文件是编译过程

编译完了以后

这样的文件是不可以执行的

它不是可执行程序

要生成可执行程序呢

还需要连接过程

这个连接过程

是将我们编译好的了这个obj文件

还有这个obj文件 多个

一个project里面经过编译以后

可能有多个obj文件

这些object文件呢

都要连接在一起

除此而外

我们不是还是调用了

系统库里面的功能吗

比如说我们用了cout

那么它就是系统库里面

预定义好的输出流对象

所以我们还需要

将系统运行库里面有关的内容

也连接在一起

最后就形成了

这样一个可执行的文件

但是大家在目前

比较流行的可视化编译环境中

去编译的时候呢

好象看不到这些细节的步骤

比如说在visual studio里面

我们要生成一个工程的话

直接生成就可以了

直接build就完成了

它会将编译和连接

都完全都给你做好

当然中间编译有错的话

会报错让你去改错

如果连接中间有文件找不到的话

也会报错 让你改错

但是如果都没有错的话

一键就build成功了

你基本上就没有注意到

它有编译过程 有连接过程

但是我们作为学习的过程

还是应该了解一下

它实质上的编译连接

是怎么样一个过程

以及在每个阶段都解决什么问题

这样的话

以后我们做稍微大一点的项目

需要有多个文件的时候

就知道怎么样把这多个文件

放到一个工程里面了

由于我们可以将一个程序中的

不同的函数

不同的类

以及它们的实现放在

不同的C++文件中

那么这个时候如果我们需要使用

在其他文件中定义的变量

可不可以呢 可以

这个时候呢我们就可以

把它作为外部变量来使用

外部变量呢

就是不在本程序文件中

定义的变量

那么如果你要使用一个在别处

定义的

在同一个工程文件中的变量呢

我们要用extern关键字来修饰

在文件作用域中的变量

默认情况下

就是可以作为外部变量的

另外函数它定义的位置

和调用的位置

也很可能不在同一个文件中

这个时候如果我们想使用

在别的文件中定义的函数

这种情况下

也叫做使用外部函数

那么定义一个函数的默认情况下

它具有全局作用域的函数

它就是可以作为外部函数的

如果我们要使用一个

在别处定义的函数

怎么办呢

只要在当前文件中

给它声明了就可以了

在使用之前

声明函数的原型就可以了

有的时候呢

我们并不希望在当前文件中

定义的这些标识符

能拿到别的文件中去使用

那这种情况下怎么办呢

我们可以把它限制在一个

命名空间中

我们可以用命名空间来限制

当前文件中定义的这些变量

函数

都只能在当前文件中使用

实际上我们在写程序中

其实本身就用到了大量

外部的这些功能模块

是什么呢

是C++的库里面

预定义好的这些功能模块

比如说我们用到库里面的函数

用到库里预定义好的类

那么C++标准库里面

到底给我们提供了哪些东西呢

标准C++库是一个极为灵活

并且可以扩展的

可重用的软件模块的集合

在逻辑上呢

这些预定义好的模块

分成六种类型

一个就是我们已经多次使用的

这个输入输出类

比如说我们用的cin对象

cout对象

就是输入输出类的

预定义好的对象

还有就是容器类和抽象数据类型

我们知道

如果一个程序

你能处理的数据

只是我单独列出来的

那么一个两个

五个十个变量

其实这个程序好象

没有太大的用途

我们经常会写程序

用来处理大批量的数据

那么这样的大批量数据

在程序中怎么存储呢

其实标准C++库

已经给我们准备好了这些

在标准库里头

有一类叫做容器类

它是一些类模板的总称

关于类模板我们会在第九章介绍

关于容器类

我们也会在第十章给大家介绍

这些预定义好的容器类模板

是非常有用的

它不仅能够存储与管理数据

而且带有很多数据操作的

有用的函数

另外一个类型就是存储管理类

还有C++标准库中

还带有着大量的预定义好的算法

比如说查找算法 排序算法

我们拿来用就行了

非常好用

还包括错误处理

以及运行环境支持

那么这些就是C++库里面的

这些预定义好的模块的

这样几大类型

现在呢

我们再来学习编译预处理命令

我们看这里列出了几个

编译预处理命令

第一个include指令

是大家比较熟悉的

它能将一个头文件包含进来

也就是把它的内容

插入到当前的位置

包含头文件有两种方式

一种是用尖括号括起文件名

表示按标准的方式搜索

文件位于C++系统目录的

include子目录底下

另外一种

就是用双引号括起文件名来

那它就会首先在当前目录中搜索

如果没有

再按标准方式搜索

好 那咱们再看这个define

这是一个宏定义指令

在C语言中

就有这样的编译预处理指令define

它可以定义符号常量

定义带参数的宏

但是这样的功能

在C++中基本上已经不用了

C++中要定义常量的话

用const

如果要定义宏的话

实际上在C++中

被内联函数取代了

那么在C++中

仍然有的时候我们会用

define这个宏定义指令

那只是为了定义一些符号 标记

用于在其他的编译预处理指令中

比如说进行选择性编译等等

那么undefine呢

undef实际上就是取消define指令

定义的那个符号

那这个if和endif构成的呢

是一个条件编译指令

也就是说

我们可以告诉编译器

当某种条件满足的时候

你就编译这一段程序正文

如果这个常量表达式为假

也就是为0

那么就不编译这一段程序正文

通过这种方式呢

我们在每一次编译之前

如果需要的话

可以重新设置

这个常量表达式的值

你把它设成0

这一段程序

编译器就会忽略它 不会编译

你把它设成非0

编译器就会编译这一段程序

所以我们也可以有选择性地

告诉编译器

在这一次编译的时候

生成这个版本的时候

我们要还是不要这一段程序

条件编译指令

还可以有两个选择

当常量表达式为非0的时候

编译程序正文这个程序段1

否则就编译程序正文2

这样就可以有目的性地

去设置这个常量表达式

然后让编译器

在我们不同的设置条件下

编译程序的不同部分

那么那个不编译的部分

就相当于我们在这一次

编译的时候

把那个代码都删掉了

可是你又没有真的删掉

你要恢复它就在那儿

这样比较方便

这个是多选择的条件定义

可以判断多个常量表达式

根据需要来设计

让编译器

到底编译哪一段程序正文

那这个ifdef跟else配合起来呢

能起到什么作用呢

能起到就是说

我看这个标识符

在这儿被定义过没有

如果定义过了

我就编译程序段1

否则就编译程序段2

那么这个标识符

到底什么叫做被定义过

很多初学的同学

就会纠结于这个标识符被定义过

它被定义成什么了

它被定义成几了

其实呢所谓被定义过

就是它通过define命令

去定义过这个标识符

但是我们并不需要

把它定义成值是几

只要这个标识符

在define中定义过

就说明我们树起了一个标签

树起了一个记号

按照这个记号所规定的意义

我们应该让编译器

去编译程序段1

否则就编译程序段2

那么也有一个反过来的

如果没有定义标识符

你去编译程序段1

否则编译程序段2

那么我们用这样的条件编译

可以在头文件中

设置这样一个标识符

如果没有被定义过这个标识符

那就编译某一段程序

同时马上把这个标识符定义一下

那如果这一个程序

被多次包含的话

当第二次包含的时候

他就会发现标识符已经被定义了

那么重复的程序段

就不会再被编译

这样就避免你多次重复include

同一个头文件的时候

造成一些类 函数

这些常量被重复定义的问题

C++语言程序设计基础课程列表:

第1章 绪论

-导学

--第1章导学

-计算机系统简介

--计算机系统简介

--计算机系统简介 测试题

-计算机语言和程序设计方法的发展

--计算机语言和程序设计方法的发展

--计算机语言和程序设计方法的发展 测试题

-面向对象的基本概念

--面向对象的基本概念

--面向对象的基本概念 测试题

-程序的开发过程

--程序的开发过程

--程序的开发过程 测试题

-信息的表示和储存

--计算机中的信息与存储单位

--计算机的数字系统

--数据的编码表示

--信息的表示和储存 测试题

-实验指导

--实验一:VS开发环境介绍

第2章 C++简单程序设计(一)

-导学

--第二章导学

-C++语言概述

--C++的特点和程序实例

--C++字符集和词法记号

--C++语言概述 测试题

-基本数据类型、常量、变量

--基本数据类型、常量、变量

--程序举例

--基本数据类型、常量、变量 测试题

-运算与表达式

--算术运算与赋值运算

--逗号运算、关系运算、逻辑运算和条件运算

--Sizeof运算、位运算

--运算优先级、类型转换

--运算与表达式 测试题

-实验二:简单程序设计(上)

--实验二:简单程序设计(上)

第2章 C++简单程序设计(二)

-数据的输入和输出

--数据的输入和输出

--数据的输入和输出 测试题

-选择结构

--if语句

--switch语句

--选择结构 测试题

-循环结构

--循环结构——while语句

--do-while语句

--for语句

--嵌套的控制结构、其他控制语句

--循环结构 测试题

-自定义类型

--自定义类型

--自定义类型

-第2章小结

--第二章小结

-实验二:C++简单程序设计(下)

--实验二C++简单程序设计(下)

第3章 函数

-导学

--导学

-函数定义

--函数定义

--函数定义 测试题

-函数调用

--函数调用(例3-1)

--例3-2

--例3-3

--例3-4

--例3-5

--例3-6

--函数调用 测试题

-嵌套与递归

--函数的嵌套调用(例3-7)

--函数的递归调用(例3-8)

--例3-9

--例3-10

--嵌套与递归 测试题

-函数的参数传递

--函数的参数传递

--函数的参数传递 测试题

-引用类型

--引用类型(例3-11)

--引用类型 测试题

-含有可变参数的函数

--含有可变参数的函数

--含有可变参数的函数 测试题

-内联函数

--内联函数(例3-14)

--内联函数 测试题

-constexpr函数

--constexpr函数

--CONSTEXPR函数课后习题

-带默认参数值的函数

--带默认参数值的函数

--默认参数值例(3-15)

--带默认参数值的函数 测试题

-函数重载

--函数重载(例3-16)

--函数重载 测试题

-C++系统函数

--C++系统函数(例3-17)

--C++系统函数习题

-第3章小结

--第三章小结

-实验三(上)函数的应用

--实验三(上)函数的应用

-实验三(下)函数的应用

--实验三(下)函数的应用

第4章 类与对象

-导学

--导学

-面向对象程序的基本特点

--面向对象程序的基本特点

--面向对象程序的基本特点 测试题

-类和对象

--类和对象的定义

--类和对象的程序举例

--类和对象 测试题

-构造函数

--构造函数基本概念

--构造函数例题(1)——例4-1

--构造函数例题(2)——例4-2

--委托构造函数

--复制构造函数

--复制构造函数调用举例

--构造函数 测试题

-析构函数

--析构函数

--析构函数 测试题

-类的组合

--类的组合

--类的组合程序举例

--前向引用声明

--类的组合 测试题

-UML简介

--UML简介

--UML简介课后习题

-结构体与联合体

--结构体(例4-7)

--联合体(例4-8)

--结构体与联合体 测试题

-枚举类

--枚举类

--枚举类 测试题

-第4章小结

--第四章小结

-实验四(上)

--实验四(上)

-实验四(下)

--实验四(下)

第5章 数据的共享与保护

-导学

--导学

-标识符的作用域与可见性

--标识符的作用域与可见性

--标识符的作用域与可见性 测试题

-对象的生存期

--对象的生存期

--对象的生存期 测试题

-类的静态成员

--静态数据成员(例5-4)

--静态函数成员(例5-5)

--类的静态成员 测试题

-类的友元

--类的友元(例5-6)

--类的友元 测试题

-共享数据的保护

--共享数据的保护(例5-7)

--共享数据的保护 测试题

-多文件结构和预编译命令

--多文件结构和预编译命令(例5-10)

--多文件结构和预编译命令 测试题

-第5章小结

--小结

-实验五

--实验五

第6章 数组、指针与字符串(一)

-导学

--导学

-数组的定义与初始化

--数组的定义与使用

--数组的储存与初始化

--一维数组应用举例

--数组的定义与初始化 测试题

-数组作为函数的参数

--数组作为函数参数(例6-2)

--数组作为函数的参数 测试题

-对象数组

--对象数组

--对象数组 测试题

-基于范围的for循环

--基于范围的for循环

-指针的定义和运算

--指针的概念、定义和指针运算

--指针的初始化和赋值

--指针的算术运算、关系运算

--指针的定义和运算 测试题

-综合实例

--综合实例

-实验六(上)

--实验六上

第6章 数组、指针与字符串(二)

-指针与数组

--用指针访问数组元素

--指针数组

--指针与数组 测试题

-指针与函数

--以指针作为函数参数

--指针类型的函数

--指向函数的指针

--指针与函数 测试题

-对象指针

--对象指针

--对象指针 测试题

-动态内存分配

--动态分配与释放内存

--申请和释放动态数组(一)

--申请和释放动态数组(二)

--动态内存分配 测试题

-智能指针

--智能指针

-vector对象

--vector对象

--vector对象 测试题

-对象复制与移动

--深层复制与浅层复制

--移动构造

--对象复制与移动 测试题

-字符串

--C风格字符串

--string类

--字符串 测试题

-第6章小结

--第六章小结

-综合实例

--综合实例

-实验六(下)

--实验六(下)

多文件结构和预编译命令(例5-10)笔记与讨论

也许你还感兴趣的课程:

© 柠檬大学-慕课导航 课程版权归原始院校所有,
本网站仅通过互联网进行慕课课程索引,不提供在线课程学习和视频,请同学们点击报名到课程提供网站进行学习。