(1)关于函数的重载、覆盖、隐藏:
重载的条件:函数的参数类型、参数个数不同,才能构成重载。
覆盖的条件:基类函数必须是虚函数(有virtual关键字);发生覆盖的两个函数要分别位于派生类和基类中;函数名称、参数列表都必须完全相同。
隐藏的条件:派生类中函数与基类函数同名(不管参数列表是否相同)。
注意:只有函数的返回值类型不同不能构成重载;当函数带有默认参数事要注意是否构成重载;重载是在同一个类中出现的。
在子类中如果对基类虚函数有覆盖,无论该覆盖是否有virtual关键字,子类中的这个函数都是虚函数。
在子类中如果对基类虚函数有隐藏,子类中的这个函数是不是虚函数由子类中的这个函数自己决定,而不管基类是否为虚函数。
简单的判断方法:函数的覆盖发生在派生类与基类之间,两个函数必须完全相同,并且都是虚函数。不属于这种情况的就是隐藏。
(2)多个头文件的问题
当出现多个头文件时容易出现重复包含,利用条件预处理指令可以解决:
注意:通常将类的定义、类成员函数的声明放在头文件.h中;将类中成员函数的实现放到源文件.cpp中。
animal.h
#ifndef ANIMAL_H_H //此处用条件预处理指令解决重复包含头文件的问题。当编译器编译1.cpp文件,在这个文件中包含了animal.h和fish.h两个头文件,
//知道animal这个类定义了,接着展开fish.h头文件,而fish.h头文件中也包含了animal.h,再次展开animal.h,于是animal这个类
#define ANIMAL_H_H //就重复定义了。
class animal
{
public:
animal();
~animal();
void eat();
void sleep();
virtual void breathe();
};
#endif
animal.cpp
#include"animal.h" //<>是不会搜索当前目录的,所以要用""。
#include<iostream.h>
//using namespace std;
animal::animal()
{
}
animal::~animal()
{
}
void animal::eat()
{
cout<<"eat!!!"<<endl;
}
void animal::sleep()
{
cout<<"sleep!!!"<<endl;
}
void animal::breathe() //在animal.h头文件中已经加了virtual,在.cpp源文件中则不需再加virtual。
{
cout<<"breathe!!!"<<endl;
}
fish.h
#include"animal.h" //fish类从animal继承而来,要让编译器知道animal是一种类的类型,就要包含animal.h头文件。
#ifndef FISH_H_H
#define FISH_H_H
class fish:public animal
{
public:
void breathe();
};
#endif
fish.cpp
#include"fish.h"
#include<iostream.h>
//using namespace std;
void fish::breathe()
{
cout<<"bubble!!!"<<endl;
}
1.cpp
#include"animal.h"
#include"fish.h"
//#include<iostream.h>
//using namespace std;
void hhh(animal *pAn)
{
pAn->breathe(); //通过基类来实现动态绑定
}
int main()
{
animal *pAn;
fish fishhhh;
pAn = &fishhhh; //c++是强类型语言,对类型检查严格。但是这里并没有违背,因为fish对象也是一个animal对象,将fish转换为animal类型
//不用强制类型转换,c++编译器会自动进行这种转换。反过来不能把animal对象看成时fish对象。
hhh(pAn);
// pAn->breathe(); //可以不调用函数hhh(),直接用此句来完成多态的效果。
return 0;
}
通过Rebuild All可以看到,产生可执行程序经历了两个步骤:
1.编译器对三个.cpp源文件进行了单独编译(Compiling...)。编译时,由预处理器对预处理指令(#include、#define、#if)进行处理,在内存中输出翻译单元。编译器接受预处理器的输出,将源代码转换成包含机器语言指令的.obj目标文件。在编译过程中,头文件不参与编译。
2.链接器将目标文件和用到的c++类库文件一起链接生成1.exe(Linking...)。