C++程序设计中,一般将类的声明,类的定义分开,将类的声明放在.h的头文件中,而将类的定义放在.cpp的源文件中,这样我们的程序设计起来更加的模块化也清晰明了。但是,这样的设计也会带来一些问题,一个典型的问题就是重复定义问题。如果从一个类派生出一个类,派生类声明时需要包含基类的头文件,如果再在主函数包含这个头文件,编译时就报错,编译器出现重复定义的问题,给出重定义错误提示。
我们先看看下面这个程序。首先我们在新建工程下新建两个.h的头文件(文件名分别为people.h和 student.h),继续新建三个.cpp的源文件(文件名分别是people.cpp、student.cpp、main.cpp),然后编辑相应的代码。
//people.h头文件
#include<iostream>
using namespace std;
class people //定义一个类people,类有一个成员函数breath()
{
public:
voidbreath();
};
//student.h头文件 //定义一个类student,从类people继承得到,重写成员函数breath()
#include"people.h"
#include<iostream>
using namespace std;
class student :public people
{
public:
voidbreath();
};
//people.cpp源文件 //定义类people的breath()方法
#include"people.h"
void people::breath()
{
cout << "poeple"<< endl;
}
//student.cpp源文件 //重写类student的breath()方法
#include"student.h"
void student::breath()
{
cout << "student"<< endl;
}
//main.cpp源文件
#include"people.h"
#include"student.h"
int main()
{
people p; //实例化类people的一个对象p
student s; //实例化类student的一个对象s
p.breath(); //调用函数
s.breath();
return 0;
}
在vs2013中对三个源文件进行分别编译,对于people.cpp和student.cpp编译时,没有错误,而对main.cpp编译时显示错误errorC2011: “people”:“class”类型重定义,说明类重复定义。我们来分析一下原因。在main.cpp源文件中,发现#include"people.h"此句代码,编译器回去查找people.h头文件,发现people这个类的定义,继续执行,执行到#include"student.h"这句代码时,编译器便会去查找student.h头文件,在student.h头文件中,编译器执行到#include" people.h"时,便又去查找people.h头文件中的代码,再次发现类people的定义,这样,由于include嵌套造成类people重复定义了两次,编译器便会报错。
对于这种轻量程序,我们可以通过将所有代码写在一个源文件中,包括类的声明和定义,还有主函数,这样就不用写#include"people.h"和#include"student.h"。但对于大程序,这种方式显然是行不通的,不但会让代码显得冗长而且没有条理。因此对于这种情况,最常用的方法是用条件编译#ifndef…#define…#endif语句。修改上面people.h和student.h文件。
//people.h头文件
#ifndef A
#define A
#include<iostream>
using namespace std;
class people
{
public:
voidbreath();
};
#endif
//student.h头文件
#ifndef B
#define B
#include"people.h"
#include<iostream>
using namespace std;
class student :public people
{
public:
voidbreath();
};
#endif
经过上面的修改,这个程序就可以通过编译了。观察改写后的代码,发现我们在类的定义前后分别加上了#ifndef…#define…#endif语句,当编译器编译main.cpp源文件中的代码,发现#include"people.h"此句代码,编译器回去查找people.h头文件,走到#ifndef B时,编译器会做出如下的判断,若A没有被定义,便定义它(#define A被执行)继续往下走,走到#include"student.h"这句代码时,编译器便会去查找student.h头文件,在student.h头文件中,编译器走到#include " people.h"时,便又去查找people.h头文件中的代码,与上面的一样,编译器会判断A定义了没有,若没有,便进行定义,反之,将跳过#ifndef…#endif间的代码,继续往下走,直到程序编译完毕。所以通过条件编译就可以解决重复定义的问题。#ifndef B语句中的B表示一个标示符,也可以用自己想用的不与程序冲突的其他标示符,通常用大写,只要每个头文件的标示符是唯一的。一般我们可以取一些表达明了的标示符,如在这里可以用 PEOPLE_H。
虽然不是所有的头文件中都要用条件编译,但是,在头文件中使用条件编译避免重复包含,只有好处没有坏处。有时候虽然重复包含一个头文件不会报错,但同样的代码多次编译也是浪费系统资源,所以尽可能地要避免这种重复编译情况。
参考文献:
http://www.360doc.com/content/12/0428/04/7775902_207173830.shtml
http://www.cnblogs.com/wangh0802PositiveANDupward/archive/2012/07/17/2595678.html