条款33:避免遮掩继承而来的名称

注意:此篇文章主要讲的是作用域的问题,重点不是继承。

首先说一下重载,重写(覆盖),重定义(隐藏)的概念区别:

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();
	}


编译器:看到名称mf2,然后开始在mf4的作用域找,如果没找到继续向外围查找,即查找Derived的作用域,如果还是没找到查找在Base作用查找,这个时候找到mf2了于是停止查找,假如没有找到则继承向外查找,查找namespac作用域中,最后在global的作用域找


下面我们对上述类进行改进重载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 声明式或转交函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值