C++多文件程序处理方法.docx
- 文档编号:16699405
- 上传时间:2023-07-16
- 格式:DOCX
- 页数:12
- 大小:20.57KB
C++多文件程序处理方法.docx
《C++多文件程序处理方法.docx》由会员分享,可在线阅读,更多相关《C++多文件程序处理方法.docx(12页珍藏版)》请在冰点文库上搜索。
C++多文件程序处理方法
C++多文件程序
多文件结构和编译预处理命令
一.C++程序的多文件结构
一个程序按结构至少可以划分为三个文件:
类的声明文件(*.h文件)、类的实现文件(*.cpp文件)和主函数文件(使用到类的文件,*.cpp文件),如果程序更复杂,我们会为每个类单独建一个声明文件和一个实现文件。
这样我们要修改某个类时就直接找到它的文件修改即可,不需要其他的文件改动。
时钟类的例子:
//文件1:
Clock类的声明,可以起名为Clock.h
#include
usingnamespacestd;
classClock//时钟类声明
{
public:
//外部接口
Clock();
voidSetTime(intNewH,intNewM,intNewS); //三个形参均具有函数原型作用域
voidShowTime();
~Clock(){}
private:
//私有数据成员
intHour,Minute,Second;
};
//文件2:
Clock类的实现,可以起名为Clock.cpp
#include"Clock.h"
//时钟类成员函数实现
Clock:
:
Clock()//构造函数
{
Hour=0;
Minute=0;
Second=0;
}
voidClock:
:
SetTime(intNewH,intNewM,intNewS)
{
Hour=NewH;
Minute=NewM;
Second=NewS;
}
voidClock:
:
ShowTime()
{
cout< "< "< } //文件3: 主函数,可以起名为main.cpp #include"Clock.h" //声明全局对象g_Clock,具有文件作用域,静态生存期 Clockg_Clock; intmain()//主函数 { cout<<"文件作用域的时钟类对象: "< //引用具有文件作用域的对象: g_Clock.ShowTime(); g_Clock.SetTime(10,20,30); ClockmyClock(g_Clock); //声明具有块作用域的对象myClock,并通过默认拷贝构造函数用g_Clock初始化myClock cout<<"块作用域的时钟类对象: "< myClock.ShowTime();//引用具有块作用域的对象 } 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文件。 在vs2010中如何生成这三个文件呢? 我们可以点菜单中Project->AddClass,在弹出的对话框中选择c++class,然后由弹出个对话框,在classname处填上类名点finish就可以了,这样.h文件和.cpp文件会自动生成,我们也可以点Project->AddNewItem,在弹出的对话框中选择HeaderFile(.h)或C++File(.cpp)来生成.h文件或.cpp文件。 二.编译预处理程序 编译器在编译源程序以前,要由预处理程序对源程序文件进行预处理。 预处理程序提供了一些编译预处理指令和预处理操作符。 预处理指令都要由“#”开头,每个预处理指令必须单独占一行,而且不能用分号结束,可以出现在程序文件中的任何位置。 1.#include指令 #include指令也叫文件包含指令,用来将另一个源文件的内容嵌入到当前源文件该点处。 其实我们一般就用此指令来包含头文件。 #include指令有两种写法: #include<文件名> 使用这种写法时,会在C++安装目录的include子目录下寻找<>中标明的文件,通常叫做按标准方式搜索。 #include"文件名" 使用这种写法时,会先在当前目录也就是当前工程的目录中寻找""中标明的文件,若没有找到,则按标准方式搜索。 2.#define和#undef指令 如果你学过C语言,就会知道用#define可以定义符号常量,比如,#definePI3.14这条指令定义了一个符号常量PI,它的值是3.14。 C++也可以这样定义符号常量,但一般更常用的是在声明时用const关键字修饰。 C语言还用#define定义参数宏,来实现简单的函数运算,比如,#defineadd(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) #defineHEAD_H 我们在包含头文件时,有时多次重复包含同一个头文件,比如下面这种情况: //main.cpp文件 #include"file1.h" #include"file2.h" intmain() { … } //file1.h文件 #include"head.h" … //file2.h文件 #include"head.h" … //head.h文件 ... classA { ... } ... 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文件 #ifndefHEAD_H #defineHEAD_H ... classA { ... } ... #endif 在这个改好的head.h文件中,上来会先判断HEAD_H是否被定义过,如果没有被定义过,则head.h文件还没参与过编译,就编译此文件中的源代码,同时定义HEAD_H,标记head.h文件已经参与过编译。 如果HEAD_H已经被定义过,则说明此文件已经参与过编译,编译器会跳过本文件左右内容编译其他部分,类A也不会有重复定义的错误了。 --C语言的要求: 函数先声明后定义 --example1: test1.c中 #include int main(void) { printf("%d\n",fun(5)); } intfun() { printf("HelloWorld\n"); } //代码编译,链接都能够通过。 但是会有警告: warningC4013: 'fun'undefined;assumingexternreturningint warningC4716: 'fun': mustreturnavalue 通过可以看出虽然fun(5)在使用前没有声明,但是编译器仅仅是给出警告,并提供默认的函数声明类型: int name(void);(参考C语言创始人的书),编译器在遇到fun(5)的“fun(”的时候就假设fun是一个函数,如果没有找到fun函数,就提供默认的函数声明,且忽略fun中的函数参数。 并在链接的过程中找到了fun函数的定义,则通过链接,生成可执行代码。 如下代码,能够通过编译,却不能通过链接,因为链接的时候找不到fun函数的定义。 #include int main(void) { printf("%d\n",fun(5)); } --注: 第一个程序在C++编译器里不能通过编译,因为C++编译器严格要求函数先声明后使用,如果找不到声明,不能通过编译,编译器并不会提供默认的函数声明。 --多文件组织中函数的先声明后定义 --test1.c #include int main(void) { printf("%d\n",fun(5)); } --test2.c intfun() { printf("HelloWorld\n"); } --上述文件在编译时能够通过编译,并且能偶通过链接,但是会产生如下警告: Test1.c: warningC4013: 'fun'undefined;assumingexternreturningint Test2.c: warningC4013: 'printf'undefined;assumingexternreturningint warningC4716: 'fun': mustreturnavalue test1.c中产生警告的原因是上面已经解释过了,链接能够通过是链接器能够自动的把一起编译的test1.c和test2.c中可执行代码链接起来,这样test1.c中的fun函数就找到了定义。 Test2.c中第一个警告是由于上述同样的原因,能够通过链接是由于test1.c中已经包含了头文件 --上述例子说明多个源文件文件在编译环境下可以通过链接器自动连接到一起并不需要头文件的参与,但是为了程序的可移植性和正确性,一定要遵循先声明后使用的原则: 这个原则不但适用于函数,也使用全局变量。 --头文件中应该包含什么 --test.h inta; voidshow(void) { printf("Helloworld\n"); } --test1.c intfun() { printf("HelloWorld\n"); } --test2.c #include #include"test.h" int main(void) { printf("%d\n",fun(5)); } 上述编译和链接同样能够通过,通过上面的例子不难理解: 因为就是在头文件中定义了全局变量,并把它包含在test2.c中; --test.h inta; voidshow(void) { printf("Helloworld\n"); } --test1.c #include“test.h ” intfun() { printf("HelloWorld\n"); } --test2.c #include #include"test.h" int main(void) { printf("%d\n",fun(5)); } --上述多文件程序能够通过编译,却不能通过链接: errorLNK2005: _showalreadydefinedintest1.obj fatalerrorLNK1169: oneormoremultiplydefinedsymbolsfound 具体原因有以下两个: 1: test1.c包含了test.h的头文件,定义了全局变量a,同理test.2也通过相同的手段定义了全局变量a; 2: test1.c和test2.c通过上述手段分别提供了show的定义 --上述分析指出在链接的过程中由于出现了同一个函数和变量的多个定义,所以链接出错。 所以在头文件不要包含函数的定义和全局变量的定义,这样做虽然有时候链接和编译都能通过,但是还是有一定的隐患。 总结,在头文件中一般只包含函数的声明和全局变量的声明。 函数的声明大家都知道,全局变量的声明大家不一定清楚: 必须显式的提供extern 如: externinta;注意在头文件中这样只是声明,并不提供实际的定义,在使用该变量的源文件中还有重新以inta;的形式重新定义,而其他使用该全局变量的源文件只需包含该头文件即可。 C++多个头文件的编译技巧介绍2010-11-0100: 00中国IT实验室佚名 关键字: C++编译头文件 企业软件热点文章 Java多线程实现异步调用 为Oracle导出文件加上时间戳 1,文件.CPP里包含的头文件要正确.改包含的要包含。 2,main函数里要包含你编写全部.H头文件。 3,如没问题参照以下步骤。 举个例子吧: 假设你已经编写好了以下3个文件(或者更多): SSList.H——头文件 SSList.cpp——(函数)实现文件(后缀为.c也可以) SSList_main.cpp——主(调用)文件(后缀为.c也可以) 并将它们放在同一个文件夹里面。 到这里准备工作,差不多了! ! ! (不管你怎么编,能编写出来就行,用记事本等等随你愿,可以最简单的: 里面什么都没有,只是写好了文件名,呵呵,够简单了吧! ! ! ) 现在,我们进行编译运行,在VC++6.0怎么办呢? 还是一句话最简单的,那就是打开了VC++6.0了这个我想不用说了执行"文件"——"打开"——然后找到刚才你存放的3个文件中任意一个。 cpp文件(你不是有VC6.0吗? .cpp文件默认是用它打开吗? 双击就行,要不,在文件上点右键——"打开方式"——选择VC6.0打开,OK! ! ! ),直接编译(buildF7或者按"有双箭头向下的按妞"),简单吧,你可能会问,那还有两个文件呢? 别急,编译后是不是一大堆错啊,这样的好处是工程它自己建立了,现在问题是,把那另外两个文件加进去! ! ! 刚才编译后,左边原来空白的工作区中,是不是有文字了,工作区下面是不是有ClassView和FileView,按一下FileView,然后在工作区中+号旁边的文字(如ex00_datafile)上点击右键——"填加文件到工程"——添加你需要的文件,加多少个文件你就重复多次——右键——添加。 到此,你可以编写你的任意一个文件了,哦,原来你已经全编写好了,那直接按那"有双箭头向下的按妞"进行编译吧! ! ! 原文出自【比特网】,转载请保留原文链接:
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C+ 文件 程序 处理 方法