1、同名覆盖的理论关键:
继承中同名覆盖问题的核心知识点:作用域问题,例子:
int a;
void dosomething(){
double a;
cin>>a; //使用double a
}
现象:内层作用域会覆盖外层作用域的同名变量,而无论变量的类型。
原因:当编译器遇到a
时,首先在local作用域查找该变量,找到则停止向外查找,而无论这个变量的类型,这就是C++名称遮掩规则。
2、C++继承中同名覆盖的情形介绍:
a) 一般继承例子:
//.h
class Base{
private:
int x;
public:
virtual void func1() = 0;
virtual void func2();
}
class Derived: public Base{
public:
virtual void func1(); //纯虚函数重写后,这样才能实例化。
void func3();
}
//.cpp
void Derived::func3(){
func2();
}
Derived
继承了Base
类,因此继承了Base
类的接口与实现,因此可以调用func2()
。
具体过程为:编译器在void Derived::func3()
函数中遇到func2()
,首先在该函数内找func2()
的定义,接着在类Derived
中查找,然后在基类Base
中找到func2()
,因此给人以派生类继承了基类接口的印象。
注:派生类赋值运算符函数实质是由于同名覆盖,然后在该函数内调用基类赋值运算符函数,利用作用域限定符修饰,令编译器到基类中查找。
b) 继承中覆盖例子:
//.h
class Base{
private:
int x;
public:
virtual void func1() = 0;
virtual void func1(int); //两个func1构成重载
virtual void func2();
virtual void func2(int); //两个func2构成重载
void func3();
void func3(int); //两个func3构成重载
}
class Derived: public Base{
public:
virtual void func1(); //纯虚函数重写后,这样才能实例化。
void func3(int,int); //构成同名覆盖
}
//.cpp
Derived d;
d.func1(); //调用派生类Derived中func1。
d.func1(2); //错误,编译器提示Derived类中不存在该函数,因此即使虚函数也被同名覆盖。
d.func2(); //调用基类Base中func2。
d.func2(5); //调用基类Base中func2(int)。都通过作用域查找调用
d.func3(5); //错误,编译器提示Derived类中不存在该函数,因为同名覆盖(无论特征标是否相同)。
d.func3(); //错误,编译器提示Derived类中不存在该函数,因为同名覆盖(无论特征标是否相同)。
d.func3(3,4); //调用派生类Derived中func3(int,int)。
注:
1、派生类void func3();
与基类void func3(int);
不构成重载,只是覆盖,因为重载的要求为在同一作用域下。
2、覆盖只是看不到,不代表不存在。
c) 取消覆盖的方法:
方法1:利用作用域运算符
//.h
class Derived: public Base{
public:
virtual void func1(); //纯虚函数重写后,这样才能实例化。
void func3(int a,int b){ Base::func3(a);...} //方式1:作用域运算符
virtual void func1(int a){Base::func1(a);} // 派生类声明同名函数,函数中调用基类同名函数,这被称为转交函数
}
方法1:利用using声明
//.h
class Derived: public Base{
public:
using Base::func1; //让基类中名为func1的所有函数都可用,无论特征标是什么
using Base::func3; //让基类中名为func3的所有函数都可用,无论特征标是什么
virtual void func1(); //纯虚函数重写后,这样才能实例化。
void func3(int a,int b){ Base::func3(a);...} //方式1:作用域运算符
}
//.cpp
Derived d;
d.func1(2); //正确,调用基类Base中func1(int)。
d.func3(5); //正确,调用基类Base中func3(int)。
d.func3(); //正确,调用基类Base中func3()。
此时基类函数均可用,使用using
声明引入同名的所有函数,无论特征标是什么。
参考资料:
总结:
1、基类与派生类函数间永远不存在重载,因重载要求两函数为同一作用域。
2、派生类出现与基类同名函数时,若基类为virtual,且特征标完全相同,此时为多态情形;
3、即使基类成员函数为虚函数,但派生类特征标与基类不同,仍然为同名覆盖。
4、基类函数不为虚函数,派生类同名,无论特征标是否相同,均为同名覆盖。
5、造成覆盖的原因为编译器查找变量名范围由小到大,找到即停止,不考虑特征标(C++名称遮掩规则)。
6、基类作用域大于派生类作用域。
7、覆盖只是看不到,不代表不存在。
8、为了让派生类中使用基类中被覆盖的函数,可以使用using
声明在派生类中引入基类同名的所有函数,无论特征标是什么;也可以利用转交函数,声明同名函数,函数中调用基类同名函数。