C++ FAQ Lite[23]--继承(你所不知道的)(更新)

原创 2001年05月26日 13:10:00

[23] 继承 — 你所不知道的
(Part of C++ FAQ Lite, Copyright © 1991-2001, Marshall Cline, cline@parashift.com)

简体中文版翻译:申旻nicrosoft@sunistudio.com东日制作室东日文档


FAQs in section [23]:


[23.1] 基类的非虚函数调用虚函数可以吗? NEW!

[Recently created (on 4/01). Click here to go to the next FAQ in the "chain" of recent changes.]

可以。有时(并非总是!)这是一个好主意。例如,假设所有Shape(图形)对象有一个公共的打印算法。但这个算法依赖于它们的面积并且它们都有不同的方法来计算面积。在这种情况下,Shapearea()方法(译注:得到Shape面积的成员函数)必须是virtual的(可能是纯虚(pure-virtual)的),但Shape::print()可以在Shape中被定义为非虚(non-virtual)的,前提是所有派生类不会需要不同的打印算法

 #include "Shape.hpp"
 
 void Shape::print() const
 {
     float a = this->area();  
// area() 为纯虚
     
// ...
 }

TopBottomPrevious sectionNext section ]


[23.2] 上面那个FAQ让我糊涂了。那是使用虚函数的另一种策略吗? NEW!

[Recently created (on 4/01). Click here to go to the next FAQ in the "chain" of recent changes.]

是的,那是不同的策略。是的,那的确是使用虚函数的两种不同的基本方法:

  1. 假设你遇到了上一个FAQ所描述的情况:每一个派生类都有一个结构完全一样,只有一小块不同的方法。因此算法是相同的,但实质不相同。在这种情况下,你最好在基类写一个全面的算法作为public:方法(有时是非虚的),然后在派生类中写那不同的一小块。这一小块在基类中声明(通常是protected:的,纯虚的,当然至少是virtual的),并且最终在每个派生类中被定义。这种情况下最紧要的问题是包含全面的算法的public:方法是否应该是virtual的。答案是,如果你认为某些派生类可能需要覆盖它,就让它成为virtual的。
  2. 假设你遇到了上一个FAQ完全相反的情况,每一个派生类都有一个结构完全不同,但有一小块的大多数(如果不是全部的话)相同的方法。在这种情况下,你最好将全面的算法放在最终在派生类中定义的public: virtual之中,并且将一小块可以被只写一次的公共代码(避免代码重复)隐藏在某处(任何地方!)。一般放在基类的protected:部分,但不是必须的,也可能不是最好的。找个地方隐藏它们就行了。注意,由于public:用户不需要/不想做它们做的事情,如果在基类中隐藏它们,通常应该使它们是protected:的。假定它们是protected:的,那么可能不应该是virtual的:如果派生类不喜欢它们之一的行为,可以不必调用这个方法。

强调一下,以上列表中的是“既/又”情况,而不是“二者选一”的。换句话说,在任何给定的类上,不必在两种策略中选择。既有一个符合策略 #1 的方法f(),又有一个符合策略 #2 的方法g()是非常正常的。换句话说,在同一个类中,有两种策略同时工作是非常正常的。

TopBottomPrevious sectionNext section ]


[23.3] 当基类构造函数调用虚函数时,为什么不调用派生类重写的该虚函数?

当基类被构造时,对象还不是一个派生类的对象,所以如果 Base::Base()调用了虚函数 virt(),则 Base::virt() 将被调用,即使 Derived::virt()(译注:即派生类重写的虚函数)存在。

同样,当基类被析构时,对象已经不再是一个派生类对象了,所以如果 Base::~Base()调用了virt(),则 Base::virt()得到控制权,而不是重写的 Derived::virt()

当你可以想象到如果 Derived::virt() 涉及到派生类的某个成员对象将造成的灾难的时候,你很快就能看到这种方法的明智。详细来说,如果 Base::Base()调用了虚函数 virt(),这个规则使得 Base::virt()被调用。如果不按照这个规则,Derived::virt()将在派生对象的派生部分被构造之前被调用,此时属于派生对象的派生部分的某个成员对象还没有被构造,而 Derived::virt()却能够访问它。这将是灾难。

TopBottomPrevious sectionNext section ]


[23.4] 派生类可以重置(“覆盖”)基类的非虚函数吗?

合法但不合理。

有经验的 C++ 程序员有时会重新定义非虚函数(例如,派生类的实现可能可以更有效地利用派生类的资源),或者为了回避隐藏规则。即使非虚函数的指派基于指针/引用的静态类型而不是指针/引用所指对象的动态类型,但其客户可见性必须是一致的。

TopBottomPrevious sectionNext section ]


[23.5] “Warning: Derived::f(float) hides Base::f(int)” 是什么意思?

意思是:你要完蛋了。

你所处的困境是:如果基类声明了一个成员函数f(int),并且派生类声明了一个成员函数 f(float)(名称相同,但参数类型和/或数量不同),那么 Base 的 f(int)被隐藏(hidden)而不是被重载(overloaded)或被重写(overridden)(即使 基类的f(int)虚拟的)

以下是你如何摆脱困境:派生类必须有一个被隐藏成员函数的using 声明,例如:

 class Base {
 public:
   void f(int);
 };
 
 class Derived : public Base {
 public:
   using Base::f;    
// This un-hides Base::f(int)
   void f(double);
 };

如果你的编译器不支持using语法,那么就重新定义基类的被隐藏的成员函数,即使它们是非虚的。一般来说这种重定义只不过使用::语法调用了基类被隐藏的成员函数,如,

 class Derived : public Base {
 public:
   void f(double);
   void f(int i) { Base::f(i); }  
// The redefinition merely calls Base::f(int)
 };

TopBottomPrevious sectionNext section ]


[23.6]  "virtual table" is an unresolved external 是什么意思?

如果你得到一个连接错误"Error: Unresolved or undefined symbols detected: virtual table for class Fred",那么可能是你在 Fred 类中有一个未定义的成员函数。

编译器通常会为含有虚函数的类创建一个称为“虚函数表”的不可思议的数据结构(这就是它如何处理动态绑定的)。通常你根本不必知道它。但如果你忘了为Fred 类定义一个虚函数,则有时会得到这个连接错误。

许多编译器将这个不可思议的“虚函数表”放进定义类的第一个非内联虚函数的编辑单元中。因此如果 Fred 类的第一个非内联虚函数是 wilma(),那么编译器会将 Fred 的虚函数表放在 Fred::wilma() 所在的编辑单元里。不幸的是如果你意外的忘了定义 Fred::wilma(),那么你会得到一个"Fred's virtual table is undefined"(Fred的虚函数表未定义)的错误而不是“Fred::wilma() is undefined”(Fred::wilma()未定义)。

TopBottomPrevious sectionNext section ]


E-Mail E-mail the author
C++ FAQ LiteTable of contentsSubject indexAbout the author©Download your own copy ]
Revised Apr 8, 2001

C++ FAQ学习笔记 23章 继承 — 你所不知道的

等待中
  • u010278548
  • u010278548
  • 2013年08月29日 16:46
  • 381

你不知道的JavaScript(上卷)pdf

下载地址:网盘下载 内容简介  · · · · · · JavaScript语言有很多复杂的概念,但却用简单的方式体现出来(比如回调函数),因此,Jav...
  • cf406061841
  • cf406061841
  • 2017年07月31日 09:56
  • 966

C++ FAQ Lite[19]--继承(基础)(更新)

[19] 继承 — 基础(Part of C++ FAQ Lite, Copyright © 1991-2001, Marshall Cline, cline@parashift.com)简体中文版翻...
  • Nicrosoft
  • Nicrosoft
  • 2001年04月23日 22:04
  • 919

C++ FAQ Lite[20]--继承(虚函数)(更新)

[20] 继承 — 虚函数(Part of C++ FAQ Lite, Copyright © 1991-2001, Marshall Cline, cline@parashift.com)简体中文版...
  • Nicrosoft
  • Nicrosoft
  • 2001年04月25日 17:24
  • 1600

C++ FAQ Lite[22]--继承(抽象基类)(更新)

[22] 继承 — 抽象基类(ABCs)(Part of C++ FAQ Lite, Copyright © 1991-2001, Marshall Cline, cline@parashift.co...
  • Nicrosoft
  • Nicrosoft
  • 2001年04月30日 12:19
  • 1034

你不知道的JavaScript(上卷)笔记

你不知道的JavaScript(上卷)笔记1.块作用域块作用域是一个用来对之前的最小授权(函数)进行扩展的工具,将代码从函数中隐藏信息扩展为在块中隐藏信息。有以下四种方式创建块作用域。1.1 with...
  • qq_27582155
  • qq_27582155
  • 2017年01月11日 09:31
  • 337

你不知道的JavaScript(上卷).pdf 免费下载

下载地址: 你不知道的JavaScript(上卷).pdf
  • jiongyi1
  • jiongyi1
  • 2018年01月27日 10:32
  • 69

你不知道的JavaScript演示代码Github地址

你不知道的JavaScript博文相关代码托管至Github,每次写完博客会把代码提交上去。代码地址:https://github.com/rongbo-j/you-dont-know-js点击Dow...
  • Rongbo_J
  • Rongbo_J
  • 2015年05月16日 22:24
  • 2337

《你不知道的JavaScript 中卷》

慢慢的,敢说自己懂 JS 了,坐等下卷变量是没有类型的,只有值才有JavaScript 中的变量是没有类型的,只有值才有,在对变量执行 typeof 操作时,得到的结果并不是该变量的类型,而是该变量持...
  • wozaixiaoximen
  • wozaixiaoximen
  • 2017年01月15日 21:50
  • 1073

C++ FAQ Lite[24]--继承(私有和保护继承)(更新)

[24] 继承 — 私有继承和保护继承(Part of C++ FAQ Lite, Copyright © 1991-2001, Marshall Cline, cline@parashift.com...
  • Nicrosoft
  • Nicrosoft
  • 2001年06月01日 05:08
  • 1109
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C++ FAQ Lite[23]--继承(你所不知道的)(更新)
举报原因:
原因补充:

(最多只允许输入30个字)