莎士比亚有一个关于名字的说法Whats 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++ 的 namehiding 规则仅仅是覆盖那个名字而相对应的名字的类型是否相同是无关紧要的在此例中一个名为 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 mf() = ; virtual void mf(); void mf(); }; class Derived: public Base { public: virtual void mf(); void mf(); }; 本例中包含的既有 public 名字也有 private 名字既有 data members 也有 member functionsmember functions 既有 pure virtual 的也有 simple (impure) virtual 的还有 nonvirtual 的那是为了强调我们谈论的事情是关于名字的 例子中还可以包括其它类型的名字例如enumsnested classes和 typedefs在这里的讨论中唯一重要的事情是它们是名字与它们是什么东西的名字毫不相关这个示例中使用了 single inheritance但是一旦你理解了在 single inheritance 下会发生什么C++ 在 multiple inheritance 下的行为就很容易预见了 假设 mf 在 derived class 中被实现其中一部分如下 void Derived::mf() { mf(); } 当编译器看到这里对名字 mf 的使用它就必须断定它指涉什么它通过搜索名为 mf 的某物的定义的作用域来做这件事首先它在 local 作用域中搜索(也就是 mf 的作用域)但是它没有找到被称为 mf 的任何东西的声明然后它搜索它的包含作用域也就是 class Derived 的作用域它依然没有找到叫做 mf 的任何东西所以它上移到它的上一层包含作用域也就是 base class 的作用域在那里它找到了名为 mf 的东西所以搜索停止如果在 Base 中没有 mf搜索还会继续首先是包含 Base 的 namespace(s)(如果有的话)最后是 global 作用域 我刚刚描述的过程虽然是正确的但它还不是一个关于 C++ 中名字如何被找到的完整的描述无论如何我们的目的不是为了充分了解关于写一个编译器时的名字搜索问题而是为了充分了解如何避免令人吃惊的意外而对于这个任务我们已经有了大量的信息 再次考虑前面的示例而且这一次我们 overload mf 和 mf并且为 Derived 增加一个 mf 的版本(Derived 对 mf ——一个通过继承得到的 nonvirtual function ——的重载使得这个设计立即变得可疑但是出于对 inheritance 之下名字可见性问题的关心我们就装作没看见) class Base { private: int x; public: virtual void mf() = ; virtual void mf(int); virtual void mf(); void mf(); void mf(double); }; class Derived: public Base { public: virtual void mf(); void mf(); void mf(); }; 以上代码导致的行为会使每一个第一次遇到它的 C++ 程序员吃惊基于作用域的名字覆盖规则(scopebased name hiding rule)不会有什么变化所以 base class 中的所有名为 mf 和 mf 的函数被 derived class 中的名为 mf 和 mf 的函数覆盖 从名字搜索的观点看Base::mf 和 Base::mf 不再被 Derived 继承! Derived d; int x; dmf(); // fine calls Derived::mf dmf(x); // error! Derived::mf hides Base::mf dmf(); // fine calls Base::mf dmf(); // fine calls Derived::mf dmf(x); // error! Derived::mf hides Base::mf 就像你看到的即使 base 和 derived classes 中的函数具有不同的参数类型它也同样适用而且不管函数是 virtual 还是 nonvirtual它也同样适用与在本文的开始处函数 someFunc 中的 double x 覆盖了 global 作用域中的 int x的道理相同这里 Derived 中的函数 mf 覆盖了具有不同类型的名为 mf 的一个 Base 函数 这一行为背后的根本原因是为了防止当你在一个 library 或者 application framework 中创建一个新的 derived class 时偶然地发生从遥远的 base classes 继承 overloads 的情况不幸的是一般情况下你是需要继承这些 overloads 的实际上如果你使用了 public inheritance 而又没有继承这些 overloads你就违反了base 和 derived classes 之间是 isa 关系这一 public inheritance 的基本原则在这种情况下你几乎总是要绕过 C++ 对通过继承得到的名字的缺省的覆盖机制 你可以用 using declarations 做到这一点 class Base { private: int x; public: virtual void mf() = ; virtual void mf(int); virtual void mf(); void mf(); void mf(double); }; class Derived: public Base { public: using Base::mf; // make all things in Base named mf and mf using Base::mf; // visible (and public) in Deriveds scope virtual void mf(); void mf(); void mf(); }; 现在 inheritance 就可以起到预期的作用 Derived d; int x; dmf(); // still fine still calls Derived::mf dmf(x); // now okay calls Base::mf dmf(); // still fine still calls Base::mf dmf(); // fine calls Derived::mf dmf(x); // now okay calls Base::mf 这意味着如果你从一个带有重载函数的 base class 继承而且你只想重定义或替换它们中的一部分你需要为每一个你不想覆盖的名字使用 using declaration如果你不这样做一些你希望继承下来的名字会被覆盖 可以想象在某些时候你不希望从你的 base classes 继承所有的函数在 public inheritance 中这是绝不会发生的这还是因为它违反了 public inheritance 在 base 和 derived classes 之间的 isa 关系(这就是为什么上面的 using declarations 在 derived class 的 public 部分在 base class 中是 public 的名字在公有继承的 derived class 中也应该是 public)然而在 private inheritance中它还是有意义的例如假设 Derived 从 Base 私有继承而且 Derived 只想继承没有参数的那个 mf 的版本在这里using declaration 没有这个本事因为一个 using declaration 会使得所有具有给定名字的函数在 derived class 中可见不这里是使用了一种不同的技术的情形即一个简单的 forwarding function(转调函数) class Base { public: virtual void mf() = ; virtual void mf(int); // as before }; class Derived: private Base { public: virtual void mf() // forwarding function; implicitly { Base::mf(); } // inline (see Item ) }; Derived d; int x; dmf(); // fine calls Derived::mf dmf(x); // error! Base::mf() is hidden forwarding function(转调函数)的另一个功效是用于老式的编译器它们(不正确地)不支持用 using declarations 将通过继承得到的名字引入到 derived class 的作用域 这就是关于 inheritance 和 name hiding 的全部故事但是当 inheritance 与 templates 结合起来通过继承得到的名字被隐藏的问题会以一种全然不同的形式呈现出来关于全部 anglebracketdemarcated(边边角角)的细节 Things to Remember ·derived classes 中的名字覆盖 base classes 中的名字在 public inheritance 中这从来不是想要的 ·为了使隐藏的名字重新可见使用 using declarations 或者 forwarding functions(转调函数) |