C++之对待继承得到的名字

一.结论

  • 派生类中变量或者函数名字覆盖基类的名字,是公有继承所不希望的
  • 使用using声明或者转调函数可以使隐藏的函数名字或者变量名字可见

二.详细说明

为了说明以上的结论,首先需要说明隐藏、覆盖以及作用域等概念,接着举例说明隐藏、覆盖以及作用域等带来的问题,最后以解决方法解决以上问题。

2.1 隐藏、覆盖和作用域

作用域:从字面上理解,某事物起作用的地方,比如说白天太阳能够照射的地方,能看到太阳,而照不到的地方就看不到太阳,能够看到太阳的地方就是太阳的作用域。比较官方的定义如下:

作用域:程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效/可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域 —百度百科

隐藏:从字面上理解就是,因为某事物的到来,一些事物被隐藏起来了,但是可以通过一些办法让它显现出来,比如还以上面的例子,太阳照射不到的地方通过镜子将阳关反射到被隐藏事物的地方(转调),隐藏的事物亦可以看到太阳了。官方的定义:

派生类将继承的基类的同名的成员变量和成员方法隐藏起来,通过派生类只能访问到自己的成员变量和成员方法。

覆盖: 从字面上理解,某些事物因为有冲突,在一些作用域下不能同时出现,在某一片阳光下,要么只能出现事物A,要么只能出现事物B。

覆盖往往与继承的类中有virtual修饰的函数有关。virtual修饰的函数为虚函数。而覆盖就是基类中有virtual修饰的函数,且在派生类中有与基类中的虚函数同名且同参数列表的函数,那么派生类中的的该函数就会将基类中的函数覆盖,调用时无法调用基类中的函数

2.2.局部变量和全局变量选择

int val = 1; // global valuevoid main(){ double val = 2.5; // local value std::cout << val << std::endl; // out 2.5 std::cout << ::val << std::endl; // out 1}

运行结果:

2.51

当编译器在cout处输出val时,将首先查找 TestFun 的作用域中是否有名字 val ,如果有则它将直接输出这个变量的值,如果没有则会向TestFun之外的地方查找val符号,找到则直接输出。此处输出2.5,局部的val变量覆盖了全局的val变量。如果要想使用全局变量val,需要添加作用域::val。

此处局部变量掩盖全局变量,若想使用全局变量可添加作用域的形式使用

2.3. 继承时变量选择

有如下类继承关系,基类中包含纯虚函数、虚函数和非虚函数,派生类公有继承基类,包含基类中非虚函数的同名同参函数和独有函数,如下:

class Base {private: int val; public: virtual void mFun1() = 0; virtual void mFun1(double) { std::cout << "Base mFun1 with param double" << std::endl; } virtual void mFun2() { std::cout << "Base mFun2" << std::endl; } void mFun3() { std::cout << "Base mFun3" << std::endl; } void mFun3(float) { std::cout << "Base mFun3 with param float" << std::endl; }};class Derived: public Base {public: virtual void mFun1() { std::cout << "Derieved mFun1" << std::endl; } void mFun3() { std::cout << "Derieved mFun3" << std::endl; mFun2(); } void mFun4() { std::cout << "Derieved mFun4" << std::endl; mFun3(); // mFun3(1.25); // no matching function }};

1.在构建派生类实例时,调用同名同参的非虚函数mFun3时,其搜索路径是,先搜索mFun3内部是否找到mFun2,如果没有找到则在类Derived中查找mFun2,如果类内有找到则继续向上Base类中查找mFun2,如果没有找到则继续向本文件中查找mFun2等等。此处mFun2在Base类中找到,如下例子:

int main() { Derived der; der.mFun3(); return 0;}

运行结果:

Derieved mFun3Base mFun2

2.在构建派生类实例时,调用函数mFun4时,mFun4中现调用的mFun3只会调用派生类Derived中的mFun3,且无法调用带参数的mFun3,否则编译时会报no matching function错误,实例中调用带参的mFun3一样报错,因为派生类中的函数覆盖了基类中的同名函数包括不同参数,换句话说派生类中的同名函数覆盖了了Base::mFun1和Base::mFun3。 如下例子:

int main() { Derived der; der.mFun1(); // call Derived mFun1 der.mFun4(); // call Derived mFun4 // der.mFun3(1.25); // no matching function return 0;}

运行结果

Derieved mFun4Derieved mFun3Base mFun2

从上面的两个例子中可以发现一个很明显的问题,由于同名覆盖的情况,导致在派生类中无法调用基类中同名不同参数的函数, 以下就是一些解决办法

2.4 使用using声明

使用using 声明可以将作用域从原先限定的地方拓展到声明的地方,增加其使用范围,例子如下:

// Base基类同上class Derived: public Base {public: using Base::mFun3; virtual void mFun1() { std::cout << "Derieved mFun1" << std::endl; } void mFun3() { std::cout << "Derieved mFun3" << std::endl; mFun2(); } void mFun4() { std::cout << "Derieved mFun4" << std::endl; mFun3(); // mFun3(1.25); // no matching function }};

使用Demo进行测试:

int main() { Derived der; der.mFun1(); // call Derived mFun1 der.mFun1(1.0); // call Base mFun1 der.mFun4(); // call Derived mFun4 der.mFun3(); // call Derived mFun3 der.mFun3(1.25); // call Base mFun3 return 0;}

运行结果:

Derieved mFun1 // der.mFun1();Base mFun1 with param double // der.mFun1(1.0);Derieved mFun4 // der.mFun4();Derieved mFun3Base mFun2Derieved mFun3 // der.mFun3();Base mFun2Base mFun3 with param float // der.mFun3(1.25);

从结果中可以看出使用using声明将基类中的函数作用域拓展到了派生类中,在派生类中可以直接使用,假如派生类中只想继承基类中不带参数的函数该如何呢?这时使用using声明可能会将带参数的函数同时给引入进来,而无法解决此类问题。此处可以通过转调函数实现,如下。

2.5 使用转掉函数

私有继承加上转调函数可以完美解决using声明不能解决的只继承带不参数的函数,例子如下:

// Base基类同上void Base::mFun1(){ std::cout << "Base mFun1" << std::endl;}class Derived: private Base {public: virtual void mFun1() { std::cout << "Derieved mFun1" << std::endl; Base::mFun1(); }};

使用Demo进行测试

int main() { Derived der; der.mFun1(); // call Derived mFun1 // der.mFun1(1.0); // error no matching function return 0;}

运行结果:

Derieved mFun1Base mFun1

参考:

1.Effective C++ Item33

2.解析C++隐藏与覆盖Oragen的博客-CSDN博客c++隐藏和覆盖

3.作用域_百度百科 (baidu.com)

备注:

欢迎大家多多指教,喜欢的大家可以点击关注本账号或者微信公众号:软件系统安全,后续将不定期更新C++相关的内容

郑重声明:本文内容及图片均整理自互联网,不代表本站立场,版权归原作者所有,如有侵权请联系管理员(admin#wlmqw.com)删除。
上一篇 2022年8月8日 10:19
下一篇 2022年8月8日 10:19

相关推荐

联系我们

联系邮箱:admin#wlmqw.com
工作时间:周一至周五,10:30-18:30,节假日休息