一、重载
概念:重载是指相同的范围中(例如一个类)不同的函数使用相同的函数名,但是函数的参数个数或类型或顺序不同。调用的时候根据函数的参数来区别不同的函数。重载函数的返回值可相同也可不同。重载在编译期就可以确定了。
注意重载是C++中独有的C语言中没有重载,要追究这一问题那就需要从编译的角度去分析这两者的不同。例如void fun(int a,int b);
这个函数在C++和C编译后的符号是不同的,C++经过编译器编译后所产生的符号为_fun_int_int,而C语言在编译器编译过后所产生的符号为_fun。从上面可以看出C++是根据函数名和参数列表来确认一个函数的与返回值没有关系,而当函数名相同参数列表不同的两个函数对C++来说是不同的两个函数。
举例说明重载的实现:
class A{
public:
void Fun(int i);
void Fun(double i); //可以
void Fun(int i, double j); //可以
void Fun(double i, int j); //可以
int Fun(int i); //不可以
};
上面的代码中第2、3、4都可以实现重载但最后一个不可以,因为C++不会根据返回值类型来确认一个函数。
二、覆盖
所谓的覆盖(重写)是指在派生类中重新对基类中的虚函数重新实现。即函数名、参数列表和返回值都一样,只是函数的实现体不一样。当派生类对象调用类中该同名函数时会自动调用派生类中的覆盖版本,而不是基类中的被覆盖函数版本。
之所以能形成重载是因为在一个类中如果有虚函数的存在那么在他实例化对象的前面便会有一个四字节大小的指针,叫做虚函数指针,这个虚函数指针后指向一个虚表虚表中存在着虚函数的入口地址,当派生类继承基类时也会继承下来基类的虚函数表,在派生类中发现重写了这个函数则把在虚函数表中覆盖掉基类中的虚函数地址换为自己的虚函数地址,由此达到了覆盖。在编译时并不会确定函数的具体位置,而在运行是时才从虚表中寻找函数的具体位置。所以覆盖实现了动多态即运行时的多态,而重载只是实现的是静多态即编译时的多态。
举例来说明覆盖的过程:
class Animal
{
public:
Animal(string name)
{
_name = name;
}
virtual void call()
{
cout << _name << "wwwwwwww" << endl;
}
virtual ~Animal()
{
}
private:
string _name;
};
class cat : public Animal
{
public:
cat(string name)
:Animal(name),_name(name)
{}
void call()
{
cout << _name<<":miao miao miao" << endl;
}
~cat()
{
}
private:
string _name;
};
class dog : public Animal
{
public:
dog(string name)
:Animal(name), _name(name)
{}
void call()
{
cout << _name<< ":wang wang wang" << endl;
}
~dog()
{
}
private:
string _name;
};
这里基类是Animal类,派生类为cat和dog类。
int main()
{
Animal * aaa = new cat("cat");
aaa->call();
Animal * bbb = new dog("dog");
bbb->call();
}
在运行这样的程序之后的结果为
这样就产生了多态,调用同一个函数call()但是产生了不同的结果。
三、隐藏
隐藏是指派生类的函数屏蔽了与其同名的基类函数。
如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏。
如果派生类的函数与基类的函数同名,并且参数也相同,此时基类函数没有virtual关键字时,基类的函数被隐藏。基类函数有virtual关键字时,基类的函数被覆盖。
同样举例来说明:
class A
{
public:
void fun(int n, int m)
{
cout << "A::fun() :" << n << " " << m << endl;
}
};
class B : public A
{
public:
void fun(double n)
{
cout << "B::fun() :" << n << endl;
}
};
int main()
{
B b;
b.fun(5); //调用B类中的函数
b.fun(1, 2); //出错基类函数此时被隐藏
return 0;
}