概述
在C++中将类以及类中的成员函数的声明放在.h头文件中,而将类中成员函数的定义(函数的实现)放在.cpp源码中,这样我们的程序设计起来会更加模块化,但是,这样会带来一些问题:比如类型重定义等。
1:我们建一个工程(建立两个头文件Animal.h和Fish.h,三个.cpp源文件:Animal.cpp,Fish.cpp,Main.cpp)
Animal.h
class Animal
{
public:
Animal(int height,int weight);
void eat();
void sleep();
virtual void breathe();
};
Fish.h
#include "Animal.h"
class Fish:public Animal
{
public:
Fish();
void breathe();
private:
};
Fish.cpp
#include<iostream>
#include"Fish.h"
using namespace std;
Fish::Fish() :Animal(300, 400)
{}
void Fish::breathe()
{
cout << "fish breathe" << endl;
}
Animal.cpp
#include<iostream>
#include"Animal.h"
using namespace std;
Animal::Animal(int height, int weight)
{
}
void Animal::eat()
{
cout << "Animal eat" << endl;
}
void Animal::sleep()
{
cout << "Animal sleep" << endl;
}
void Animal::breathe()
{
cout << "Animal breathe" << endl;
}
Main.cpp
#include "Animal.h"
#include "Fish.h"
using namespace std;
void fun(Animal* pAn)
{
pAn->breathe();
}
int main(){
Fish fh;
Animal* pAn;
pAn = &fh;
fun(pAn);
}
2: 上述代码运行会报如下错误:
Animal : "class" 类型重定义
3:分析下源码,为啥会报 Animal类重复定义
- 首先是执行Main.cpp源文件中的代码,发现#include "Animal.h"此句代码,编译器回去查找Animal.h头文件,发现Animal这个类已经定义,
- 继续执行,执行到#include "Fish.h"这句代码时,编译器便会去查找Fish.h头文件,在Fish.h头文件中,编译器执行到#include "Animal.h"时,便又去查找Animal.h头文件中的代码,再次发现类Animal的定义,
- 这样,编译器感觉类Animal重复定义了两次,于是,编译器便会报错。
4:如何解决类 Animal重复定义的问题
分析程序手动删除 多余include 引入的 Animal类定义
- 既然我们已经分析了出现这种问题的原因,那么,我们可以发现在Main.cpp文件中,我们引用了#include "Animal.h"头文件,而当我们再次去引用#include "Fish.h"头文件时,发现在Fish.h文件中也引用了#include "Animal.h"头文件,
- 既然这样,我们何不在Main.cpp文件中将#include "Animal.h"注释掉,这样就避免了重复定义的问题了,
- 其实这样做也是可以的,但是,我们想想,如果我们在写一个大型的程序时,往往有几十个甚至成百上千个的类,其中的继承关系又是那么的复杂的时候,我们便会很难分析到那块可以不写(注释掉),所以,这种方法不适合大型程序的设计。于是,我们又想出了下面的方法:
4.1 :在头文件中增加 #ifndef .... #define ....#ednif
Animal.h
#ifndefANIMAL_H_H
#defineANIMAL_H_H
class Animal
{
public :
Animal(int height, int weight) ;
void eat() ;
void sleep() ;
virtual void breathe() ;
} ;
#endif
Fish.h
#include"Animal.h"
#ifndef FISH_H_H
#define FISH_H_H
class Fish :public Animal
{
public :
Fish() ;
void breathe() ;
} ;
#endif
- 观察改写后的代码,发现我们在类的定义前后分别加上了#ifndef…#define…#endif语句,哪么这条语句有什么作用呢?
- 还是刚才的分析过程,当编译器去执行Main.cpp源文件中的代码,发现#include "Animal.h"此句代码,编译器回去查找Animal.h头文件,
- 执行到#ifndef ANIMAL_H_H时,编译器会做出如下的判断,若ANIMAL_H_H没有被定义,便定义它(#define ANIMAL_H_H被执行)继续执行,
- 执行到#include "Fish.h"这句代码时,编译器便会去查找Fish.h头文件,在Fish.h头文件中,编译器执行到#include "Animal.h"时,便又去查找Animal.h头文件中的代码,
- 与上面的一样,执行到#ifndef ANIMAL_H_H时,编译器会判断ANIMAL_H_H定义了没有,若没有,便进行定义,反之,将跳过#ifndef…#endif间的代码,继续向后执行,知道程序执行完毕。
- 显然在第一次 include"Animal.h" 时,#ifndef ANIMAL_H_H (ANIMAL_H_H 是没有被定义的),这个时候会将 Animal.h 内容拷贝到 Main.cpp 中,当再次 include"Fish.h"时,这个时候再次执行 #ifndef ANIMAL_H_H(ANIMAL_H_H 是有定义的,会直接跳出 中间的代码),走到#endif ,所以就不会报重复定义的问题
4.2 添加#pragma once程序编译一次
Animal.h
#pragma once
class Animal
{
public :
Animal(int height, int weight) ;
void eat() ;
void sleep() ;
virtual void breathe() ;
} ;
Fish.h
#pragma once
class Fish :public Animal
{
public :
Fish() ;
void breathe() ;
} ;
预处理阶段:
- 遇到①时,打开Animial.h,将#pragma once后面的内容包含进main.c中,关Animial.h。
- 遇到②时,打开Fish.h ,当试图打开 Animal.h时,发现在步骤一已经定义了一次,编译器直接跳过该语句,执行后面的语句,从而避免重复包含。
思考:
讲完了文件的重复包含,让我们来思考一个问题:如前所说,避免头文件的重复包含可以有效地避免变量的重复定义,其实不光是变量的重复定义,也可以避免函数和类、结构体的重复定义。但是
避免头文件的重复包含是否一定可以避免变量、函数、类、结构体的重复定义?
请看下面文章:C++:重定义:符号重定义:变量重定义