巧用头文件,多文件编译少发愁|浅谈实用“StdAfx” - 程序设计 - 如鹏网 计算机专...

来源:百度文库 编辑:神马文学网 时间:2024/04/27 08:01:16
巧用头文件,多文件编译少发愁|浅谈实用“StdAfx”
本文旨在分享我在编程过程中对于多文件编译的一点经验。文中谈到了因头文件重复包含而引起的重复定义问题及其解决方案,另外由此引伸出我对于StdAfx的相关认识。
如果你同我一样,有颗强烈的好奇心;如果你同我一样,刚接触Visual C++ 6.0不久;如果你同我一样,对使用AppWizard(应用程序向导)创建程序模板时生成的“StdAfx.cpp”、“StdAfx.h”感到疑惑不解。那么我会很开心,不要误会,我是开心于下面这些文字也许对你有点帮助。如果你还没有接触Visual C++ 6.0,那也没关系,只要你在编程中使用过自定义头文件,有过多文件编译的经历,那么下面这些文字同样值得一阅。
一、缘于重复包含的重复定义错误
1、“重复包含”是个啥?
“重复包含”顾名思义,即头文件在一个源文件或多个源文件中被多次包含,使得在编译时需要对该头文件中的代码进行重复编译,显然这是一种重复劳动,缺乏效率,而且还可能引起一系列问题。如果对我给出的解释感到晕乎,那么先来看几个重复包含引发错误的例子:
(1)C语言中的重复包含
例1.1.1(单源文件的重复包含):
创建一个源文件main.c,一个头文件head.h,如下:
//****************************
//*    头文件:head.h    *
//****************************
int g_iNum = 1;
//**************************** //*    头文件:head.h    * //**************************** int g_iNum = 1;
//****************************
//*    源文件:main.c    *
//****************************
#include 
#include 
#include "head.h"
#include "head.h"
// 重复包含头文件head.h
int main()
{
printf(“%d\n”, g_iNum);
system(“pause”);
return 0;
}
//**************************** //*    源文件:main.c    * //**************************** #include #include #include "head.h" #include "head.h" // 重复包含头文件head.h int main() {     printf(“%d\n”, g_iNum);     system(“pause”);     return 0; }
对例1.1.1的进行编译,编译器报错,提示类似“redefined”(重复定义)这样的错误。这是因为头文件head.h在源文件main.c中重复包含两次,编译器处理时,头文件head.h在源文件main.c中被展开,由于是展开两次,因此语句“int g_iNum = 1;”出现两次,大致情况如下:
// 其他部分
int g_iNum = 1;
int g_iNum = 1;
// 重复定义
int main()
{
printf(“%d\n”, g_iNum);
system(“pause”);
return 0;
}
// 其他部分 int g_iNum = 1; int g_iNum = 1; // 重复定义 int main() {     printf(“%d\n”, g_iNum);     system(“pause”);     return 0; }
显然这样是错误的,int型变量g_iNum在源文件main.c中连续定义两次。当然,这种错误一般人都不会犯,谁会故意将一个头文件在同一个源文件中包含两次呢?但是,如果将一个头文件包含到另一个头文件里,再将这两个头文件分别包含在一个源文件中,这样所产生的错误就比较常发生了,而且不容易察觉到,也许你就曾经犯过这样的错误。来看一下这种错误的例子。
例1.1.2(单源文件的头文件嵌套包含):
创建一个源文件main.c,两个头文件head1.h和head2.h,如下:
//******************************
//*    头文件:head1.h    *
//******************************
int g_iNum1 = 1;
//****************************** //*    头文件:head1.h    * //****************************** int g_iNum1 = 1;
//******************************
//*    头文件:head2.h    *
//******************************
#include “head1.h”
// 嵌套包含头文件head1.h
int g_iNum2 = 2;
//****************************** //*    头文件:head2.h    * //****************************** #include “head1.h” // 嵌套包含头文件head1.h int g_iNum2 = 2;
//****************************
//*    源文件:main.c    *
//****************************
#include 
#include 
#include "head1.h"
#include "head2.h"
int main()
{
printf(“%d\n”, g_iNum);
system(“pause”);
return 0;
}
//**************************** //*    源文件:main.c    * //**************************** #include #include #include "head1.h" #include "head2.h" int main() {     printf(“%d\n”, g_iNum);     system(“pause”);     return 0; }
在例1.1.2中,如果单独观察每个文件,不容易发现问题所在,需要假想编译器处理时的情况。首先头文件head1.h在源文件main.c中展开,于是源文件main.c中有拥有了语句“int g_iNum = 1;”。接着展开头文件head2.h,由于头文件head2.h中包含了头文件head1.h,故再次对头文件head1.h进行展开。此时,源文件main.c中将拥有两条“int g_iNum = 1;”语句,显然int型变量“g_iNum”被重复定义,编译器报错。
一般的解决方案是将头包含命令“#include “head1.h””从头文件head2.h中删除。
(2)C++中的重复包含
因为C++是兼容C语言的,所以上面的两个例子产生的问题在C++中同样会发生。而较之于单纯的C语言,C++中重复包问题更复杂。
我们通常在定义类时,将类的声明和实现分开,声明部分放在头文件中,实现部分放在源文件中。这是一种好习惯,但是没处理好却也会让人大伤脑筋。来看下面的几个例子:
例1.1.3(单源文件的类声明头文件嵌套包含):
创建工程,定义一个点类CPoint,将声明和实现分别放在CPoint.h和CPoint.cpp中,定义一个线类CLine,将声明和实现分别放在CLine.h和CLine.cpp中,主函数在源文件main.cpp中:
//********************************************
//*    头文件:CPoint.h                     *
//*    描  述:点类CPoint的声明    *
//********************************************
class CPoint
{
protected:
double m_dX;
double m_dY;
public:
CPoint(double x = 0, double y = 0);
virtual void set(double x, double y);
double getX();
double getY();
};
//******************************************** //*    头文件:CPoint.h                     * //*    描  述:点类CPoint的声明    * //******************************************** class CPoint {     protected:         double m_dX;         double m_dY;     public:         CPoint(double x = 0, double y = 0);         virtual void set(double x, double y);     double getX();         double getY(); };
//********************************************
//*    源文件:CPoint.cpp                  *
//*    描  述:点类CPoint的实现    *
//********************************************
#include "CPoint.h"
CPoint::CPoint(double x, double y):m_dX(x), m_dY(y)
{
}
void CPoint::set(double x, double y)
{
m_dX = x;
m_dY = y;
}
double CPoint::getX()
{
return m_dX;
}
double CPoint::getY()
{
return m_dY;
}
//******************************************** //*    源文件:CPoint.cpp                  * //*    描  述:点类CPoint的实现    * //******************************************** #include "CPoint.h" CPoint::CPoint(double x, double y):m_dX(x), m_dY(y) { } void CPoint::set(double x, double y) {     m_dX = x;     m_dY = y; } double CPoint::getX() {     return m_dX; } double CPoint::getY() {     return m_dY; }
//*********************************************************************************
//*    头文件:CLine.h                                                                     *
//*    描  述:线类CLine的声明,CLine由CPoint的派生而来。    *
//*********************************************************************************
#include "CPoint.h"
// 头文件嵌套包含
class CLine:public CPoint
{
protected:
double m_dLength;
public:
CLine(double x = 0, double y = 0, double l = 0);
void set(double x, double y, double l);
double getLength();
};
//********************************************************************************* //*    头文件:CLine.h                                                                     * //*    描  述:线类CLine的声明,CLine由CPoint的派生而来。    * //********************************************************************************* #include "CPoint.h" // 头文件嵌套包含 class CLine:public CPoint {     protected:         double m_dLength;     public:         CLine(double x = 0, double y = 0, double l = 0);         void set(double x, double y, double l);         double getLength(); };
//*******************************************
//*    源文件:CLine.cpp                  *
//*    描  述:线类CLine的实现    *
//*******************************************
#include "CLine.h"
CLine::CLine(double x, double y, double l):CPoint(x, y)
{
m_dLength = l;
}
void CLine::set(double x, double y, double l)
{
CPoint::set(x, y);
m_dLength = l;
}
double CLine::getLength()
{
return m_dLength;
}
//******************************************* //*    源文件:CLine.cpp                  * //*    描  述:线类CLine的实现    * //******************************************* #include "CLine.h" CLine::CLine(double x, double y, double l):CPoint(x, y) {     m_dLength = l; } void CLine::set(double x, double y, double l) {     CPoint::set(x, y);     m_dLength = l; } double CLine::getLength() {     return m_dLength; }
//*******************************
//*    源文件:main.cpp    *
//*******************************
#include 
#include 
#include "CPoint.h"
#include "CLine.h"
using namespace std;
int main()
{
CLine line;
line.set(1.2, 2.3, 3.4);
cout << line.getX() << endl;
cout << line.getY() << endl;
cout << line.getLength() << endl;
system(“pause”);
return 0;
}
//******************************* //*    源文件:main.cpp    * //******************************* #include #include #include "CPoint.h" #include "CLine.h" using namespace std; int main() {     CLine line;     line.set(1.2, 2.3, 3.4);     cout << line.getX() << endl;     cout << line.getY() << endl;     cout << line.getLength() << endl;     system(“pause”);     return 0; }
对例1.1.3的工程进行编译,编译器报错,提示“CPoint”类重复定义。问题出在头文件CLine.h中,由于在其中嵌套包含了头文件CPoint.h,使得在编译源文件main.cpp时出错。首先,编译器遇到头包含命令“#include "CPoint.h"”,将头文件CPoint.h在源文件main.cpp中展开。接着,遇到头包含命令“#include "CLine.h"”,也将头文件CLine.h在源文件main.cpp中展开。由于头文件CLine.h中有头包含命令“#include "CPoint.h"”,于是再次将头文件CPoint.h源文件main.cpp中展开。此时,点类CPoint的声明部分在源文件main.cpp中出现了两次,重复了,于是报错。
对于这种错误,一般有两种解决方案。一是将头文件CLine.h中的头包含命令“#include "CPoint.h"”删除,再在源文件CLine.cpp中的头包含命令“#include “CLine.h””之上添加头包含命令“#include "CPoint.h"”。二是直接将源文件main.cpp中的头包含命令“#include "CPoint.h"”删除。对于简单的几个文件而言,此方案足矣。然而对于一个包含很多文件的大工程工程,此法并非良策。在文章后面将会介绍到一种优越的解决方案。
如果是将类的声明和实现都放在一个文件中会怎样呢?看下面例1.1.4:
例1.1.4(类的声明和实现不分开):
修改例1.1.3中的错误,并将类的声明和定义都放在同一个头文件中。
//*****************************************************
//*    头文件:CPoint.h                                *
//*    描  述:点类CPoint的声明和实现    *
//*****************************************************
// 类的声明部分
class CPoint
{
protected:
double m_dX;
double m_dY;
public:
CPoint(double x = 0, double y = 0);
virtual void set(double x, double y);
double getX();
double getY();
};
// 类的实现部分
CPoint::CPoint(double x, double y):m_dX(x), m_dY(y)
{
}
void CPoint::set(double x, double y)
{
m_dX = x;
m_dY = y;
}
double CPoint::getX()
{
return m_dX;
}
double CPoint::getY()
{
return m_dY;
}
//***************************************************** //*    头文件:CPoint.h                                * //*    描  述:点类CPoint的声明和实现    * //***************************************************** // 类的声明部分 class CPoint {      protected:         double m_dX;         double m_dY;     public:         CPoint(double x = 0, double y = 0);         virtual void set(double x, double y);     double getX();     double getY(); }; // 类的实现部分 CPoint::CPoint(double x, double y):m_dX(x), m_dY(y) { } void CPoint::set(double x, double y) {      m_dX = x;      m_dY = y; } double CPoint::getX() {      return m_dX; } double CPoint::getY() {     return m_dY; }
//***************************************************************
//*        头文件:CLine.h                                                *
//*        描  述:线类CLine的声明和实现,CLine由CPoint的派生而来。*
//***************************************************************
#include "CPoint.h"
// 类的声明部分
class CLine:public CPoint
{
protected:
double m_dLength;
public:
CLine(double x = 0, double y = 0, double l = 0);
void set(double x, double y, double l);
double getLength();
};
// 类的实现部分
CLine::CLine(double x, double y, double l):CPoint(x, y)
{
m_dLength = l;
}
void CLine::set(double x, double y, double l)
{
CPoint::set(x, y);
m_dLength = l;
}
double CLine::getLength()
{
return m_dLength;
}
//*************************************************************** //*        头文件:CLine.h                                                * //*        描  述:线类CLine的声明和实现,CLine由CPoint的派生而来。* //*************************************************************** #include "CPoint.h" // 类的声明部分 class CLine:public CPoint {     protected:         double m_dLength;     public:         CLine(double x = 0, double y = 0, double l = 0);         void set(double x, double y, double l);         double getLength(); }; // 类的实现部分 CLine::CLine(double x, double y, double l):CPoint(x, y) {     m_dLength = l; } void CLine::set(double x, double y, double l) {     CPoint::set(x, y);     m_dLength = l; } double CLine::getLength() {     return m_dLength; }
//*******************************
//*        源文件:main.cpp        *
//*******************************
#include 
#include 
// 不再包含头文件CPoint.h
#include "CLine.h"
using namespace std;
int main()
{
CLine line;
line.set(1.2, 2.3, 3.4);
cout << line.getX() << endl;
cout << line.getY() << endl;
cout << line.getLength() << endl;
system(“pause”);
return 0;
}
//******************************* //*        源文件:main.cpp        * //******************************* #include #include // 不再包含头文件CPoint.h #include "CLine.h" using namespace std; int main() {     CLine line;     line.set(1.2, 2.3, 3.4);     cout << line.getX() << endl;     cout << line.getY() << endl;     cout << line.getLength() << endl;     system(“pause”);     return 0; }
将例1.1.4同例1.1.3比较,似乎例1.1.4更加简洁方便,可以少写些头包含命令。既然如此,那为什么平常我们会被建议将类的声明和实现分开来呢?这并不是空穴来风、混淆视听。当工程中含有多个源文件,而有两个或两个以上的源文件中包含了同一个类的头文件(包括声明和实现),那么问题就会凸显出来。为了演示这个问题,我们在例1.1.4的基础上,再加入一个源文件test.cpp和对应的头文件test.h,并在其中引用“CLine”这个类。
//*******************************
//*        源文件:test.cpp        *
//*******************************
#include 
#include "CLine.h"
// 在源文件main.c中也包含头文件CLine.h
using namespace std;
void test()
{
CLine line;
line.set(2.4, 1.5, 4.7);
cout << line.getX() << endl;
cout << line.getY() << endl;
cout << line.getLength() << endl;
}
//******************************* //*        源文件:test.cpp        * //******************************* #include #include "CLine.h" // 在源文件main.c中也包含头文件CLine.h using namespace std; void test() {     CLine line;     line.set(2.4, 1.5, 4.7);     cout << line.getX() << endl;     cout << line.getY() << endl;     cout << line.getLength() << endl; }
//***************************************
//*        头文件:test.h                        *
//*        描  述:test.cpp中的函数声明        *
//***************************************
void test();
//*************************************** //*        头文件:test.h                        * //*        描  述:test.cpp中的函数声明        * //*************************************** void test();
如果对源文件test.cpp单独编译不会出错,而当构建整个工程时却出错了,错误提示大意是CPoint类和CLine类的成员函数已经在目标文件main.obj中定义了。为什么会这样呢?让我们追随编译器的脚步来看个究竟:编译器先对每个源文件单独编译,由于源文件main.cpp和源文件你test.cpp中都有头包含命令“#include “CLine.h””,对头文件CLine.h进行展开后,在这两个源文件文件中都存在点类CPoint和线类CLine的声明和实现代码,这一点并不影响各个文件单独编译。单独编译之后得到两个目标文件main.obj和test.obj。接下来将目标文件连接为一个文件,连接过程中发现在该文件中点类CPoint和线类CLine都被定义了两次,于是就报错。
此时只得把按更正后的例1.1.3将点类CPoint和线类CLine的声明和实现分开来。因此,“将类的声明部分放在头文件中,而将实现部分放在源文件中”是很有必要的,特别是在多文件编程时。
尽管如此,然而对于类模板却是另一番景象,类模板的声明和实现需放在同一个头文件中。从网络上的资料得知,如果要将类模板的声明部分和实现部分分开的话,需要使用关键字export。但是VC6等编译器都不支持关键字export,所以目前只能将二者放在一个头文件中。
2、解决重复包含的优化方案
当一个工程的文件不多时,上述几个重复定义的解决方案并不会表现出明显的缺陷。而随着工程中文件的不断增加,这些解决方案便越发显得笨拙而费时,甚至让人无从下手。有没有更好的解决方案呢?当然是有的,否则就不会有这篇文章了!
接触过VC的朋友可能会发现,在使用AppWizard(应用程序向导)创建程序模板时会自动生成源文件StdAfx.cpp和头文件StdAfx.h。如果你的好奇心够强烈的话,也许你会百度一下,了解有关StdAfx的相关资料。在此期间,或许你对阅读这些资料时感到吃力,不知所云。既然这样,那就先撇开那堆晦涩难懂的文字,来了解一下StdAfx对我们解决重复包含问题的巨大贡献吧。下面不讲理论,只浅显地模仿StdAfx的模式来解决重复包含问题。若是理论,我也谈不来。
对于工程,我们大可不必借助程序模板,自己动手,一步步地完善一个空工程。这里就以修正后的例1.1.3为基础,进行完善。
步骤一:
再另外创建两个文件,其中一个源文件StdAfx.cpp,一个头文件StdAfx.h。(这两个文件的文件名可随意取,并不是固定的,我只是仿照了VC中自动创建的那两个文件,这样来得亲切,而且有助于理解“StdAfx”。)
步骤二:
在每个头文件的首尾处添加条件编译命令,使得每个头文件中的代码都被夹在中间,像下面这样:
//***************************************
//*        头文件:CPoint.h                *
//*        描  述:点类CPoint的声明        *
//***************************************
// 条件编译
#ifndef CPOINT_H
#define CPOINT_H
class CPoint
{
protected:
double m_dX;
double m_dY;
public:
CPoint(double x = 0, double y = 0);
virtual void set(double x, double y);
double getX();
double getY();
};
#endif
//*************************************** //*        头文件:CPoint.h                * //*        描  述:点类CPoint的声明        * //*************************************** // 条件编译 #ifndef CPOINT_H #define CPOINT_H class CPoint {     protected:         double m_dX;         double m_dY;     public:         CPoint(double x = 0, double y = 0);         virtual void set(double x, double y);         double getX();         double getY(); }; #endif
//***************************************************************
//*        头文件:CLine.h                                                *
//*        描  述:线类CLine的声明,CLine由CPoint的派生而来。        *
//***************************************************************
// 条件编译
#ifndef CLINE_H
#define CLINE_H
class CLine:public CPoint
{
protected:
double m_dLength;
public:
CLine(double x = 0, double y = 0, double l = 0);
void set(double x, double y, double l);
double getLength();
};
#endif
//*************************************************************** //*        头文件:CLine.h                                                * //*        描  述:线类CLine的声明,CLine由CPoint的派生而来。        * //*************************************************************** // 条件编译 #ifndef CLINE_H #define CLINE_H class CLine:public CPoint {     protected:         double m_dLength;     public:         CLine(double x = 0, double y = 0, double l = 0);         void set(double x, double y, double l);         double getLength(); }; #endif
这些命令的作用是防止头文件重复包含。拿头文件CLine.h为例来稍微描述一下过程:当编译器在源文件(cpp文件)中第一次遇到头包含命令“#include “CLine.h””时,打开头文件CLine.h,准备对其在源文件中进行展开、编译。此时发现宏“CLINE_H”没有被定义,于是先定义这个宏,再将其后的代码进行编译,直到遇到“#endif”命令为止。当编译器在源文件中第二次遇到头包含命令“#include “CLine.h””时,再次打开头文件CLine.h,准备对其在源文件中进行展开、编译。然而此时发现宏“CLINE_H”已经被定义过了,于是后面的代码被跳过,不编译,直接跳至“#endif”命令。这样中间的代码就不会被再次编译了,也就不会出现重复定义的问题。
另外补充一下,除了“#ifndef”和“#endif”这对条件编译命令外,还可以使用其他类似功能的条件编译指令。下面几种方式都可用于避免重复包含:
#ifndef MACRO
#define MACRO
// ……
// 代码
// ……
#endif
#ifndef MACRO #define MACRO // …… // 代码 // …… #endif
#if !define(MACRO)
#define MACRO
// ……
// 代码
// ……
#endif
#if !define(MACRO) #define MACRO // …… // 代码 // …… #endif
#pragma once
// ……
// 代码
// ……
#pragma once // …… // 代码 // ……
前两种是等价的,后一种中“#pragma once”的意思是:在编译一个源文件时,只对该文件包含(打开)一次。至于它于前两种的区别,不是本文主题所在,这里就不讲了。
步骤三:
删除所有文件(源文件和头文件)中的头包含命令。并在头文件StdAfx.h中包含当前工程所需要的所有头文件。如下:
//*******************************
//*        头文件:StdAfx.h        *
//*        描  述:包含所有头文件        *
//*******************************
// 条件编译
#ifndef STDAFX_H
#define STDAFX_H
// 包含工程所需所有头文件
#include 
#include 
using namespace std;
#include "CPoint.h"
#include "CLine.h"
#include “test.h”
#endif STDAFX_H
//******************************* //*        头文件:StdAfx.h        * //*        描  述:包含所有头文件        * //******************************* // 条件编译 #ifndef STDAFX_H #define STDAFX_H // 包含工程所需所有头文件 #include #include using namespace std; #include "CPoint.h" #include "CLine.h" #include “test.h” #endif STDAFX_H
步骤四:
在所有源文件(cpp文件或者c文件)首部包含头文件StdAfx.h,且仅包含这个头文件。
到此,工程已经可以很好的工作了,而且上面的四个步骤对于C语言和C++,对于其他编译环境(如gcc、g++,不只在vc中)都适用。我们来整体看一下我们修改后的各个文件:
//***************************************
//*        头文件:CPoint.h                *
//*        描  述:点类CPoint的声明        *
//***************************************
// 条件编译
#ifndef CPOINT_H
#define CPOINT_H
class CPoint
{
protected:
double m_dX;
double m_dY;
public:
CPoint(double x = 0, double y = 0);
virtual void set(double x, double y);
double getX();
double getY();
};
#endif
//*************************************** //*        头文件:CPoint.h                * //*        描  述:点类CPoint的声明        * //*************************************** // 条件编译 #ifndef CPOINT_H #define CPOINT_H class CPoint {     protected:         double m_dX;         double m_dY;     public:         CPoint(double x = 0, double y = 0);         virtual void set(double x, double y);         double getX();         double getY(); }; #endif
//***************************************************************
//*        头文件:CLine.h                                                *
//*        描  述:线类CLine的声明,CLine由CPoint的派生而来。        *
//***************************************************************
// 条件编译
#ifndef CLINE_H
#define CLINE_H
class CLine:public CPoint
{
protected:
double m_dLength;
public:
CLine(double x = 0, double y = 0, double l = 0);
void set(double x, double y, double l);
double getLength();
};
#endif
//*************************************************************** //*        头文件:CLine.h                                                * //*        描  述:线类CLine的声明,CLine由CPoint的派生而来。        * //*************************************************************** // 条件编译 #ifndef CLINE_H #define CLINE_H class CLine:public CPoint {     protected:         double m_dLength;     public:         CLine(double x = 0, double y = 0, double l = 0);         void set(double x, double y, double l);         double getLength(); }; #endif
//*******************************
//*        头文件:StdAfx.h        *
//*        描  述:包含所有头文件        *
//*******************************
// 条件编译
#ifndef STDAFX_H
#define STDAFX_H
// 包含工程所需所有头文件
#include 
#include 
using namespace std;
#include "CPoint.h"
#include "CLine.h"
#include “test.h”
#endif STDAFX_H
//******************************* //*        头文件:StdAfx.h        * //*        描  述:包含所有头文件        * //******************************* // 条件编译 #ifndef STDAFX_H #define STDAFX_H // 包含工程所需所有头文件 #include #include using namespace std; #include "CPoint.h" #include "CLine.h" #include “test.h” #endif STDAFX_H
//***************************************
//*        头文件:test.h                        *
//*        描  述:test.cpp中的函数声明        *
//***************************************
#ifndef TEST_H
#define TEST_H
void test();
#endif
//*************************************** //*        头文件:test.h                        * //*        描  述:test.cpp中的函数声明        * //*************************************** #ifndef TEST_H #define TEST_H void test(); #endif
//*******************************
//*        源文件:main.cpp        *
//*******************************
#include "StdAfx.h"
int main()
{
CLine line;
line.set(1.2, 2.3, 3.4);
cout << line.getX() << endl;
cout << line.getY() << endl;
cout << line.getLength() << endl;
test();
system(“pause”);
return 0;
}
//******************************* //*        源文件:main.cpp        * //******************************* #include "StdAfx.h" int main() {     CLine line;     line.set(1.2, 2.3, 3.4);     cout << line.getX() << endl;     cout << line.getY() << endl;     cout << line.getLength() << endl;     test();     system(“pause”);     return 0; }
//*******************************
//*        源文件:test.cpp        *
//*******************************
#include "StdAfx.h"
void test()
{
CLine line;
line.set(2.4, 1.5, 4.7);
cout << line.getX() << endl;
cout << line.getY() << endl;
cout << line.getLength() << endl;
}
//******************************* //*        源文件:test.cpp        * //******************************* #include "StdAfx.h" void test() { CLine line;     line.set(2.4, 1.5, 4.7);     cout << line.getX() << endl;     cout << line.getY() << endl;     cout << line.getLength() << endl; }
//*******************************
//*        源文件:StdAfx.cpp        *
//*******************************
#include "StdAfx.h"
//******************************* //*        源文件:StdAfx.cpp        * //******************************* #include "StdAfx.h"
二、神行太保——预编译头文件
1、剥开StdAfx的神秘面纱
如果你潜意识里感觉有什么东西不太搭调的话,那么你直觉是正确的。到此为止,都看不出源文件StdAfx.cpp有何功用,难道它是多余的?其实不然,它在VC中可以发挥更大的作用,可以使得一个庞大的工程在一次漫长的编译过后,接下来的编译更畅快,大大提高编译效率。不过要发挥它的作用必须借助于VC的环境设置,并不是所有开发环境都支持该功能。
下面是摘用“百度百科”的几段话来说明StaAfx的作用。如果没看明白也没关系,接下来不讲理论,只谈浅显的运用。
“Microsoft C 和 C++ 编译器提供了用于预编译任何 C 或 C++ 代码(包括内联代码)的选项。利用此性能特性,可以编译稳定的代码体,将已编译状态的代码存储在文件中,以及在随后的编译中,将预编译的代码与仍在开发的代码结合起来。由于不需要重新编译稳定代码,因此后面每次编译的速度都要快一些。”
“    VC创建项目时自动创建的预编译头文件,在编译其他文件之前,VC先预编译此文件。头文件stdafx.h引入了项目中需要的一些通用的头文件,比如window.h等,在自己的头文件中包括stdafx.h就包含了那些通用的头文件。
所谓头文件预编译,就是把一个工程(Project)中使用的一些MFC标准头文件(如Windows.H、Afxwin.H)预先编译,以后该工程编译时,不再编译这部分头文件,仅仅使用预编译的结果。这样可以加快编译速度,节省时间。
预编译头文件通过编译stdafx.cpp生成,以工程名命名,由于预编译的头文件的后缀是“pch”,所以编译结果文件是projectname.pch。
编译器通过一个头文件stdafx.h来使用预编译头文件。stdafx.h这个头文件名是可以在project的编译设置里指定的。编译器认为,所有在指令#include "stdafx.h"前的代码都是预编译的,它跳过#include "stdafx. h"指令,使用projectname.pch编译这条指令之后的所有代码。”
如果你想从理论上了解StdAfx的作用的话,可以参阅以下文章:
《关于#include "stdafx.h"》
http://blog.csdn.net/magicsutra/archive/2007/10/24/1842301.aspx
《百度百科——stdafx》
http://baike.baidu.com/view/1499221.htm?fr=ala0_1
如果你对上述参考阅读不感兴趣,那么接下来我们就来看一下如何使StdAfx.cpp发挥作用。
2、领头羊StdAfx.cpp——创建预编译头文件
步骤一:
鼠标左键单击Visual C++6.0菜单栏上的“Project”(工程),在弹出的菜单中选择“Settings”(设置),弹出“Project Settings”(工程设置)对话框。另外也可以鼠标右键单击“FileView”(文件视图)中的项目图标,在弹出的菜单中选择“Settings”,同样也可以打开“Project Settings”对话框。
步骤二:
在“Project Settings”对话框中选择“C/C++”选项卡。在该选项卡菜单中的“Category”(类别)列表框中选择“Precompiled Headers”(预编译头)项,出现对应选项。
步骤三:
在“Precompiled Headers”的对应选项中选择“Use pecompiled hader file (.pch)”(使用预编译头文件)单选按钮。在对应的“Through header”(通过头文件)编辑框中输入我们前面创建的头文件StdAfx.h。(前面已经提到,这个头文件可以任意命名,所以输入时依你自己的情况而定。)
下载 (55.81 KB)
2009-12-26 17:20
步骤四:
在“Project Settings”对话框左侧展开工程的文件树。接着展开“Source Files”(源文件)文件夹,选择我们之前创建的源文件StdAfx.cpp(该文件名依情况而定)。
步骤五:
在“Project Settings”对话框右侧与源文件StdAfx.cpp 对应的选项中选择“C/C++”选项卡。在该选项卡菜单中的“Category”(类别)列表框中选择“Precompiled Headers”(预编译头)项,于是出现对应选项。
步骤六:
在“Precompiled Headers”对应选项中选择“Create pecompiled hader file (.pch)”(创建预编译头文件)单选按钮。在对应的“Through header”(通过头文件)编辑框中输入我们前面创建的头文件StdAfx.h。
下载 (55.4 KB)
2009-12-26 17:20
步骤七:
单击“OK”(确定)按钮,完成设置。
此后便可使用预编译头文件,发挥StdAfx.cpp的作用。重新编译整个工程,在工程目录下的“Debug”文件夹下会发现一个以该工程命名的.pch文件,它便是预编译头文件。看得出,其实预编译头文件并不是.h头文件。
下载 (69.96 KB)
2009-12-26 17:20
需要注意的是,其后在对头文件StdAfx.h进行改动后,须将整个StdAfx.cpp再重新编译一次,以更新预编译头文件,否则可能在单独编译其他源文件时可能会报错!
三、篇后语
不知我的描述是否让你明白,希望本文对你有所帮助。如果你大致明白上述文字的意思,那么不妨动手实践一下。实践过程中如果发现您的结果与本文有所出入,敬请指出,欢迎一起交流讨论。
最后补充下我们鹏友的相关文章链接:
《【分享】预编译头文件,你知道多少》
http://www.rupeng.com/forum/thread-3703-1-1-uid2477.html
《visual C++头文件stdafx.h》
http://www.rupeng.com/forum/thread-1283-1-1-uid2477.html
以上两篇都是好帖,可惜发帖时间比较早,当时咱还是菜鸟,以致石沉大海,在这里给顶起来!