微信公众号: 星点课堂
新浪微博:女儿叫老白
网易云课堂:女儿叫老白
网易云课堂免费课程:《C++跨平台开发中的编译错误》
网易云课堂免费课程:《C++老鸟日记》
----------------------------------------------------------------------------
(chapter 7 PPT)
引言:
----------------------------------------------------------------------------
前面的章节中,我们讨论了重载,今天我们来讨论一下重载、覆盖、隐藏之间有什么区别。
正文:
----------------------------------------------------------------------------
我们先来看一段代码,
// parent.h
class CParent {
public:
void f(int)
{
cout << "CParent::f(int)" << endl;
}
};
// child.h
class CChild : public CParent
{
public:
void f(int,int)
{
cout << "CChild::f(int,int)" << endl;
}
void test()
{
f(1);
}
};
int main(int argc, char* argv[])
{
return 0;
}
编译了一下,报错:error C2660: 'f' : function does not take 1 parameters。
错误代码在CChild的test()接口中,在f(1)处。错误指明,函数f()不是只有一个参数。
这就奇怪了,CChild虽然提供了带两个参数的f(int, int),但是它是从CParent派生的,而CParent拥有f(int),这可是只有一个参数啊。
这就涉及到编译器的接口查找规则:在调用一个类的成员函数的时候,编译器会沿着类的继承链逐级的向上查找函数的定义,如果找到了就停止查找。所以如果一个派生类和一个基类都有同一个同名(不论参数是否相同)的函数,编译器最终选择在派生类中的函数。那么我们就说这个派生类的成员函数"隐藏"了基类的成员函数,也就是它阻止了编译器继续向上查找函数的定义。
在本例中,编译器在CChild类中找到了一个f(),虽然这个f()长得是f(int, int)的样子,并不是test()接口中调用的带一个参数的f(),但是编译器仍然会停止查找。它认定找到的这个f(int, int)就是它想要的,因此就报错了。
这就是隐藏的概念:
1. 在类CChild这个域中,定义了f(int, int)而没有定义f(int)这样的函数,基类中的void f(int)被隐藏
2. 如果把派生CChild中成员函数void f(int,int)的声明改成和基类中一样,即f(int),基类中的void f(int)还是一样被隐藏,此时编译不会出错,
结论:
在基类中的函数,函数名是f(参数是什么我们不管),那么如果在派生类CChild中也声明了某个f()成员函数,那么在类CChild域中,基类中所有的那些f()都被隐藏。
隐藏说完了,我们来说一下覆盖:
// parent.h
class CParent {
public:
virtual void f(int)
{
cout << "CParent::f(int)" << endl;
}
};
// child.h
class CChild : public CParent
{
public:
void f(int)
{
cout << "CChild::f(int)" << endl;
}
};
int main(int argc, char* argv[])
{
CChild cild;
child.f(1);
return 0;
}
我们可以看到,基类定义了一个f(int),派生类也定义了f(int)。但是基类的f(int)前面使用了virtual关键字进行声明,这说明该接口是虚的,派生类可以重新实现。因此,main()函数中,child.f(1)实际上调用了派生类的f(1),这就是覆盖,派生类覆盖了基类的同名(参数也必须完全一致)接口,这本质上是多态的概念。
如果我们把派生类的f(int)改成f(int, int)呢?大家可以自己试一下,结果也是报错:
error C2660: 'f' : function does not take 1 parameters。
是不是很面熟啊?这跟前面一样,也是派生类的f(int, int)把基类的f(int)隐藏隐藏了。
那么,隐藏和覆盖到底怎么分辨呢?
如果基类中的函数和派生类中的两个名字一样的函数f满足下面的两个条件:
1. 在基类中函数声明的时候有virtual关键字
2. 基类CParent中的函数和派生类CChild中的函数一模一样,函数名,参数,返回类型都一样。那么这就是叫做覆盖(override),这也就是虚函数,多态的性质。
那么其他的情况呢?只要名字一样,不满足上面覆盖的条件,就是隐藏了。
那么,重载又是怎么回事呢?重载:
1. 必须在一个域中,函数名称相同但是函数参数不同
2. 重载的作用就是同一个函数有不同的行为,
不在一个域中的函数是无法构成重载的,这个是重载的重要特征。
相同的函数名的函数,在基类和派生类中的关系只能是覆盖或者隐藏。
结语:
----------------------------------------------------------------------------
重载是在一个域中,同名函数之间的关系。也就是类内部的接口之间的关系。
覆盖和隐藏指不同域之间的关系。就是基类和派生类之间。
覆盖指派生类的同名同参数接口覆盖了基类的同名同参数的虚函数,也就是编译器优先找派生类同名接口,隐藏指派生类的同名接口隐藏了基类的同名接口。