注意:此篇文章主要讲的是作用域的问题,重点不是继承。
首先说一下重载,重写(覆盖),重定义(隐藏)的概念区别:
1.重载:①在相同的作用域中②函数名字相同,参数列表不同,不能靠返回类型来判断是否重载
2.重写(覆盖):位于基类和派生类①被重写的函数不能是static,必须是virtual。②重写的函数必须有相同的类型,参数列表,函数名。
3.重定义(隐藏):位于基类和派生类①如果函数不是一模一样,不管有没有virtual都是重定义②函数一模一样,没有virtual重定义
下面开始条款的内容:
1.一个例子来说明作用域掩盖问题
解释:输出的是1.35而不是10,因为内层作用域的名称会掩盖外围作用域的名字,当编译器处于fun的作用域中现在fun的作用域查找x,如果没有的话在去其他的作用域找。
C++的名称掩盖规则所做的唯一一件事就是:掩盖名称,置于名称的类型是否相同或不同不重要。
2.引入继承的作用域掩盖问题
class Base
{
private:
int x;
public:
virtual void mf1() = 0;
virtual void mf2();
void fm3();
};
class Derived :public Base
{
public:
virtual void mf1();
void mf4();
};
假设在mf4中进行这样的操作
void mf4()
{
mf2();
}
下面我们对上述类进行改进重载mf1和mf3,并且添加一个新的mf3到派生类中
class Base
{
private:
int x;
public:
virtual void mf1() = 0;
virtual void mf1(int);
virtual void mf2();
void fm3();
void fm3(int);
};
class Derived :public Base
{
public:
virtual void mf1();
void mf3();
void mf4();
};
int main()
{
Derived d;
int x;
d.mf1();//正确,调用Derived的mf1
d.mf1(x);//错误。Base中的带参数的mf1被隐藏
d.mf2();//没问题,调用Base的mf2
d.mf3();//没问题调用Derived的mf3
d.mf3(x);//错误Base中带参数的mf3被派生类的mf3隐藏
}
在这段代码中所有名字为mf1和mf3的基类函数都会被派生类的 mf1和mf3函数遮掩掉,即使基类中有带参数的mf1和mf3,而且不论是否为virtual函数都适用,所以我们可以知道,基类的函数部分函数被派生类屏蔽了,这里岂不是违反了is-a的关系?所以我们就需要为继承而来名字造成的遮掩问题进行处理
处理方式1:使用using
class Base
{
private:
int x;
public:
virtual void mf1() = 0;
virtual void mf1(int);
virtual void mf2();
void mf3();
void mf3(int);
};
class Derived :public Base
{
public:
using Base::mf1;
using Base::mf3;
virtual void mf1();
void mf3();
void mf4();
};
int main()
{
Derived d;
int x=10;
d.mf1();//正确,调用Derived的mf1
d.mf1(x);//正确调用基类带参数的mf1
d.mf2();//没问题,调用Base的mf2
d.mf3();//没问题调用Derived的mf3
d.mf3(x);//正确调用基类的带参数的mf3
}
这里意味着想
继承基类以及加上其重载形式,又希望重写定义或者覆盖其中一部分,那么你必须为那些原本会被遮掩的每个名称引入一个using 声明式
2.处理方式2:
如果你不想继承Base的所有函数,在public继承绝不可能发生,因为违反了is-a的关系,那么可以选择采用private,假设Derived以及private形式继承Base,而Derived唯一想要的是mf2的那个无需参数的版本,using声明式在这里派不上用场,因为using 声明式会令继承而来的某给定的名称所有的同名函数在DeriveD可见,所以我们采用一个简单的转交函数
class Base
{
private:
int x;
public:
virtual void mf1() = 0;
virtual void mf1(int) { cout << "Base mf1(int)" << endl; }
virtual void mf2() { cout << " Base mf2()" << endl; }
void mf3() { cout << "Base mf3()" << endl; }
void mf3(double) { cout << "Base mf3(double)" << endl; }
};
class Derived : private Base
{
public:
virtual void mf1() {
Base::mf2();
}
void mf3() { cout << "Derived mf3()" << endl; }
void mf4() { cout << "Derived mf4()" << endl; }
};
int main(void)
{
Derived d;
int x = 0;
d.mf1(); // 调用派生类mf1(),实际调用基类的mf2()
d.mf3(); // 调用派生类mf3()
system("pause");
return 0;
}
所以为了让被遮掩的名称重见天日,可使用using 声明式或转交函数