科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道应用软件避免覆盖通过继承得到的名字

避免覆盖通过继承得到的名字

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

在C++中,我们该用哪种态度对待通过继承得到的名字呢?本文通过程序实例说明事情的实质与继承没什么关系,与作用域有关。

作者:中国IT实验室 来源:中国IT实验室 2007年9月14日

关键字: 继承 名字

  • 评论
  • 分享微博
  • 分享邮件

在本页阅读全文(共19页)

莎士比亚有一个关于名字的说法。“What’s in a name?”他问道,“A rose by any other name would smell as sweet.”(语出《罗密欧与朱丽叶》第二幕第二场,朱生豪先生译为:“姓名本来是没有意义的;我们叫做玫瑰的这一种花,要是换了个名字,他的香味还是同样的芬芳。”梁实秋先生译为:“姓算什么?我们所谓有玫瑰,换个名字,还是一样的香。”——译者注)。莎翁也写过“he that filches from me my good name ... makes me poor indeed.”(语出《奥塞罗》第三幕第三场,朱生豪先生译为:“可是谁偷去了我的名誉,那么他虽然并不因此而富足,我却因为失去它而成为赤贫了。”梁实秋先生译为:“但是他若夺去我的名誉,于他不见有利,对我却是一件损失哩。”——译者注)。好吧!在C++中,我们该用哪种态度对待通过继承得到的名字呢?

事情的实质与继承没什么关系。它与作用域有关。我们都知道它在代码中是这样的, 

int x; // global variable 

void someFunc()

{

double x; // local variable

std::cin >> x; // read a new value for local x

}

读入x的语句指涉local变量x,而不是global变量x,因为内层作用域的名字覆盖(“遮蔽”)外层作用域的名字。我们可以像这样形象地表示作用域的状况:

当编译器在someFunc的作用域中遇到名字x时,他们巡视local作用域看看是否有什么东西叫这个名字。因为那里有,它们就不再检查其它作用域。在此例中,someFunc的x类型为double,而global x类型为int,但这不要紧。C++的name-hiding规则仅仅是覆盖那个名字。而相对应的名字的类型是否相同是无关紧要的。在此例中,一个名为x的double覆盖了一个名为x的int。

加入inheritance以后。我们知道当我们在一个derived class member function内指涉位于base class内的一件东西(例如,一个member function,一个typedef,或者一个data member)时,编译器能够找到我们指涉的东西是因为derived classes继承到声明于base classes中的东西。实际中的运作方法是将derived class的作用域嵌套在base class作用域之中。例如:

class Base {

private:

 int x;

public:

 virtual void mf1() = 0;

 virtual void mf2();

 void mf3();

 ...

};

class Derived: public Base {

 public:

virtual void mf1();

void mf4();

...

};

本例中包含的既有public名字也有private名字,既有data members也有member functions。member functions既有pure virtual的,也有simple (impure) virtual的,还有non-virtual的。那是为了强调我们谈论的事情是关于名字的。例子中还可以包括其它类型的名字,例如,enums,nested classes,和typedefs。在这里的讨论中唯一重要的事情是“它们是名字”。与它们是什么东西的名字毫不相关。这个示例中使用了single inheritance,但是一旦你理解了在single inheritance下会发生什么,C++在multiple inheritance下的行为就很容易预见了。

假设mf4在derived class中被实现,其中一部分,如下:

void Derived::mf4()

{

...

mf2();

...

}

当编译器看到这里对名字mf2的使用,它就必须断定它指涉什么。它通过搜索名为mf2的某物的定义的作用域来做这件事。首先它在local作用域中搜索(也就是mf4的作用域),但是它没有找到被称为mf2的任何东西的声明。然后它搜索它的包含作用域,也就是class Derived的作用域。它依然没有找到叫做mf2的任何东西,所以它上移到它的上一层包含作用域,也就是base class的作用域。在那里它找到了名为mf2的东西,所以搜索停止。如果在Base中没有mf2,搜索还会继续,首先是包含Base的namespace(s)(如果有的话),最后是global作用域。

我刚刚描述的过程虽然是正确的,但它还不是一个关于C++中名字如何被找到的完整的描述。无论如何,我们的目的不是为了充分了解关于写一个编译器时的名字搜索问题。而是为了充分了解如何避免令人吃惊的意外,而对于这个任务,我们已经有了大量的信息。

再次考虑前面的示例,而且这一次我们overload mf1和mf3,并且为Derived增加一个mf3的版本。(Derived对mf3——一个通过继承得到的non-virtual function——的重载,使得这个设计立即变得可疑,但是出于对inheritance之下名字可见性问题的关心,我们就装作没看见。)

class Base {

private:

 int x;

public:

 virtual void mf1() = 0;

 virtual void mf1(int);

 virtual void mf2();

 void mf3();

 void mf3(double);

 ...

};

class Derived: public Base {

public:

 virtual void mf1();

 void mf3();

 void mf4();

 ...

};

以上代码导致的行为会使每一个第一次遇到它的C++程序员吃惊。基于作用域的名字覆盖规则(scope-based name hiding rule)不会有什么变化,所以base class中的所有名为mf1和mf3的函数被derived class中的名为mf1和mf3的函数覆盖。从名字搜索的观点看,Base::mf1和Base::mf3不再被Derived继承!

Derived d;

int x;

...

d.mf1(); // fine, calls Derived::mf1

d.mf1(x); // error! Derived::mf1 hides Base::mf1

d.mf2(); // fine, calls Base::mf2

d.mf3(); // fine, calls Derived::mf3

d.mf3(x); // error! Derived::mf3 hides Base::mf3

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章