⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 [21] inheritance proper inheritance and substitutability, c++ faq lite.htm

📁 c++faq。里面有很多关于c++的问题的解答。
💻 HTM
📖 第 1 页 / 共 2 页
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd">
<!-- saved from url=(0056)http://www.sunistudio.com/cppfaq/proper-inheritance.html -->
<HTML><HEAD><TITLE>[21] Inheritance proper inheritance and substitutability, C++ FAQ Lite</TITLE>
<META http-equiv=Content-Type content="text/html; charset=gb2312">
<META http-equiv=Content-Language content=zh-cn>
<META content=proper-inheritance.html name=FILENAME>
<META 
content="[21] Inheritance proper inheritance and substitutability, C++ FAQ Lite" 
name=ABSTRACT>
<META content=cline@parashift.com name=OWNER>
<META content="Marshall Cline, cline@parashift.com" name=AUTHOR>
<META content="MSHTML 6.00.2462.0" name=GENERATOR>
<META content=FrontPage.Editor.Document name=ProgId><LINK rev=made 
href="mailto:cline@parashift.com"><LINK 
href="[21] Inheritance proper inheritance and substitutability, C++ FAQ Lite.files/cpp-faq.css" 
type=text/css rel=stylesheet></HEAD>
<BODY>
<H1><A name=top></A>[21] 继承 — 彻底继承和可置换性<BR><SMALL><SMALL>(Part of <A 
href="http://www.sunistudio.com/cppfaq/index.html"><EM>C++ FAQ Lite</EM></A>, <A 
href="http://www.sunistudio.com/cppfaq/copy-permissions.html#[1.2]">Copyright&nbsp;&copy; 
1991-2001</A>, <A href="http://www.parashift.com/" target=OutsideTheFAQ>Marshall 
Cline</A>, <A 
href="mailto:cline@parashift.com">cline@parashift.com</A>)</SMALL></SMALL></H1>
<P>简体中文版翻译:<A href="http://www.sunistudio.com/nicrosoft">申旻</A>,<A 
href="mailto:nicrosoft@sunistudio.com">nicrosoft@sunistudio.com</A>(<A 
href="http://www.sunistudio.com/">东日制作室</A>,<A 
href="http://www.sunistudio.com/asp/sunidoc.asp">东日文档</A>)</P>
<HR>

<H3>FAQs in section [21]:</H3>
<UL>
  <LI><A 
  href="http://www.sunistudio.com/cppfaq/proper-inheritance.html#[21.1]">[21.1] 
  我应该隐藏基类的公有成员函数吗?</A> 
  <LI><A 
  href="http://www.sunistudio.com/cppfaq/proper-inheritance.html#[21.2]">[21.2] 
  <TT>Derived*</TT> —&gt; <TT>Base*</TT> 
  可以很好地工作;&nbsp;为什么&nbsp;<TT>Derived**</TT> —&gt; <TT>Base**</TT>&nbsp;不行?</A> 
  <LI><A 
  href="http://www.sunistudio.com/cppfaq/proper-inheritance.html#[21.3]">[21.3] 
  parking-lot-of-Car(停车场)是一种&nbsp;parking-lot-of-Vehicle(交通工具停泊场)吗?</A> 
  <LI><A 
  href="http://www.sunistudio.com/cppfaq/proper-inheritance.html#[21.4]">[21.4] 
  <TT>Derived</TT>数组是一种&nbsp;<TT>Base</TT>数组吗?</A><IMG alt=UPDATED! 
  src="[21] Inheritance proper inheritance and substitutability, C++ FAQ Lite.files/updated.gif"> 

  <LI><A 
  href="http://www.sunistudio.com/cppfaq/proper-inheritance.html#[21.5]">[21.5] 
  派生类数组(array-of-<TT>Derived</TT>)“不是一种”基类数组(array-of-<TT>Base</TT>)是否意味着数组不好?</A><IMG 
  alt=UPDATED! 
  src="[21] Inheritance proper inheritance and substitutability, C++ FAQ Lite.files/updated.gif"> 

  <LI><A 
  href="http://www.sunistudio.com/cppfaq/proper-inheritance.html#[21.6]">[21.6] 
  <TT>Circle</TT>(圆)是一种 <TT>Ellipse</TT>(椭圆)吗?</A> 
  <LI><A 
  href="http://www.sunistudio.com/cppfaq/proper-inheritance.html#[21.7]">[21.7] 
  对于“圆是/不是一种椭圆”这个两难问题,有其它说法吗?</A> 
  <LI><A 
  href="http://www.sunistudio.com/cppfaq/proper-inheritance.html#[21.8]">[21.8] 
  但我是数学博士,我相信圆是一种椭圆!这是否意味着Marshall&nbsp;Cline是傻瓜?或者C++是傻瓜?或者OO是傻瓜?</A> 
  <LI><A 
  href="http://www.sunistudio.com/cppfaq/proper-inheritance.html#[21.9]">[21.9] 
  也许椭圆应该从圆继承?</A><IMG alt=NEW! 
  src="[21] Inheritance proper inheritance and substitutability, C++ FAQ Lite.files/new.gif"> 

  <LI><A 
  href="http://www.sunistudio.com/cppfaq/proper-inheritance.html#[21.10]">[21.10] 
  但我的问题与圆和椭圆无关,这种无聊的例子对我有什么好处?</A> </LI></UL>
<P>
<HR>

<P><A name=[21.1]></A>
<DIV class=FaqTitle>
<H3>[21.1] 我应该隐藏基类的公有成员函数吗?</H3></DIV>
<P>不要,不要,不要这样做。永远不要!<BR><BR>试图隐藏(消除、废除、私有化)继承而来的公有成员函数是非常常见的设计错误。通常这产生于浆糊脑袋。 
<P>(注意:&nbsp;本&nbsp;FAQ&nbsp;的论述仅与公有继承(<TT>public</TT> inheritance)有关;&nbsp;<A 
href="http://www.sunistudio.com/cppfaq/private-inheritance.html">私有和保护继承</A>并不相同) 

<P><SMALL>[&nbsp;<A 
href="http://www.sunistudio.com/cppfaq/proper-inheritance.html#top">Top</A> 
|&nbsp;<A 
href="http://www.sunistudio.com/cppfaq/proper-inheritance.html#bottom">Bottom</A> 
|&nbsp;<A 
href="http://www.sunistudio.com/cppfaq/virtual-functions.html">Previous&nbsp;section</A> 
|&nbsp;<A 
href="http://www.sunistudio.com/cppfaq/abcs.html">Next&nbsp;section</A> 
]</SMALL> 
<HR>

<P><A name=[21.2]></A>
<DIV class=FaqTitle>
<H3>[21.2] <TT>Derived*</TT> —&gt; <TT>Base*</TT> 
可以很好地工作;&nbsp;为什么&nbsp;<TT>Derived**</TT> —&gt; 
<TT>Base**</TT>&nbsp;不行?</H3></DIV>
<P>由于<TT>Derived</TT>对象是一种<TT>Base</TT>对象,C++允许<TT>Derived*</TT>&nbsp;转换成&nbsp;<TT>Base*</TT>。然而,将&nbsp;<TT>Derived**</TT><TT> 
</TT>转换成 <TT>Base** 
</TT>将产生错误。尽管这个错误不是显而易见的,这未尝不是件好事。例如,如果你能够将<TT>Car**</TT>转换成&nbsp;<TT>Vehicle**</TT><TT>(译注:Vehicle意为交通工具)</TT>,并且如果你能同样的将<TT>NuclearSubmarine**(译注:NuclearSubmarine意为核潜艇)</TT>&nbsp;转换成<TT>Vehicle**</TT>,那么你可能给这两个指针赋值,并最终使&nbsp;<TT>Car*</TT>&nbsp;指针指向&nbsp;<TT>NuclearSubmarine</TT>:
<P> 
<DIV 
class=CodeBlock><TT>&nbsp;class&nbsp;Vehicle&nbsp;{<BR>&nbsp;public:<BR>&nbsp;&nbsp;&nbsp;virtual&nbsp;~Vehicle()&nbsp;{&nbsp;}<BR>&nbsp;&nbsp;&nbsp;virtual&nbsp;void&nbsp;startEngine()&nbsp;=&nbsp;0;<BR>&nbsp;};<BR>&nbsp;<BR>&nbsp;class&nbsp;Car&nbsp;:&nbsp;public&nbsp;Vehicle&nbsp;{<BR>&nbsp;public:<BR>&nbsp;&nbsp;&nbsp;virtual&nbsp;void&nbsp;startEngine();<BR>&nbsp;&nbsp;&nbsp;virtual&nbsp;void&nbsp;openGasCap();<BR>&nbsp;};<BR>&nbsp;<BR>&nbsp;class&nbsp;NuclearSubmarine&nbsp;:&nbsp;public&nbsp;Vehicle&nbsp;{<BR>&nbsp;public:<BR>&nbsp;&nbsp;&nbsp;virtual&nbsp;void&nbsp;startEngine();<BR>&nbsp;&nbsp;&nbsp;virtual&nbsp;void&nbsp;fireNuclearMissle();<BR>&nbsp;};<BR>&nbsp;<BR>&nbsp;int&nbsp;main()<BR>&nbsp;{<BR>&nbsp;&nbsp;&nbsp;Car&nbsp;&nbsp;&nbsp;car;<BR>&nbsp;&nbsp;&nbsp;Car*&nbsp;&nbsp;carPtr&nbsp;=&nbsp;&amp;car;<BR>&nbsp;&nbsp;&nbsp;Car**&nbsp;carPtrPtr&nbsp;=&nbsp;&amp;carPtr;<BR>&nbsp;&nbsp;&nbsp;Vehicle**&nbsp;vehiclePtrPtr&nbsp;=&nbsp;carPtrPtr;&nbsp;&nbsp;</TT><EM>//&nbsp;</EM><I>这在C++中是一个错误</I><TT><BR>&nbsp;&nbsp;&nbsp;NuclearSubmarine&nbsp;&nbsp;sub;<BR>&nbsp;&nbsp;&nbsp;NuclearSubmarine*&nbsp;subPtr&nbsp;=&nbsp;&amp;sub;<BR>&nbsp;&nbsp;&nbsp;*vehiclePtrPtr&nbsp;=&nbsp;subPtr;<BR>&nbsp;&nbsp;&nbsp;</TT><EM>//&nbsp;最后这行将导致carPtr指向&nbsp;sub&nbsp;!</EM><TT><BR>&nbsp;&nbsp;&nbsp;carPtr-&gt;openGasCap();&nbsp;&nbsp;</TT><EM>//&nbsp;这将调用 
<TT>fireNuclearMissle()</TT>! (译注:也就是发射核弹)</EM><TT><BR>&nbsp;} </TT></DIV>
<P>换句话说,如果从<TT>Derived**</TT>&nbsp;到<TT>Base**</TT>的转换是合法的,那么<TT>Base**</TT>将可能被解除引用(易变的&nbsp;<TT>Base*</TT>),并且&nbsp;<TT>Base*</TT>可能被指向不同的派生类对象,这将导致严重的国家安全问题(天知道如果你调用了<TT>NuclearSubmarine</TT>(核潜艇)对象的&nbsp;<TT>openGasCap()</TT>成员函数会发生什么!!而你却认为这是一个<TT>Car</TT>对象!!——试一下以上的代码,看看会发生什么——大多数的编译器会调用<TT>NuclearSubmarine::fireNuclearMissle()</TT>! 

<P>(注意:&nbsp;本&nbsp;FAQ&nbsp;的论述仅与公有继承(<TT>public</TT> inheritance)有关;&nbsp;<A 
href="http://www.sunistudio.com/cppfaq/private-inheritance.html">私有和保护继承</A>并不相同) 

<P><SMALL>[&nbsp;<A 
href="http://www.sunistudio.com/cppfaq/proper-inheritance.html#top">Top</A> 
|&nbsp;<A 
href="http://www.sunistudio.com/cppfaq/proper-inheritance.html#bottom">Bottom</A> 
|&nbsp;<A 
href="http://www.sunistudio.com/cppfaq/virtual-functions.html">Previous&nbsp;section</A> 
|&nbsp;<A 
href="http://www.sunistudio.com/cppfaq/abcs.html">Next&nbsp;section</A> 
]</SMALL> 
<HR>

<P><A name=[21.3]></A>
<DIV class=FaqTitle>
<H3>[21.3] 
parking-lot-of-Car(停车场)是一种&nbsp;parking-lot-of-Vehicle(交通工具停泊场)吗?</H3></DIV>
<P>不。
<P>我知道这听起来很奇怪,但这是事实。你可以将这看作为以上&nbsp;FAQ的直接结论,或者你可以这样来理解:如果这个“是一种”关系成立的话,那么就可以将&nbsp;parking-lot-of-Vehicle 
类型的指针指向一个&nbsp;parking-lot-of-Car。但是,parking-lot-of-Vehicle 有 
<TT>addNewVehicleToParkingLot(Vehicle&amp;)</TT>成员函数用来向停泊场添加任何 
<TT>Vehicle</TT>(交通工具)对象。这样将允许你在&nbsp;parking-lot-of-Car(停车场)停泊一个<TT>NuclearSubmarine</TT>(核潜艇)。当然,当某人认为从&nbsp;parking-lot-of-Car 
删除一个<TT>Car</TT>对象,而实际是一个<TT>NuclearSubmarine</TT>时,他会非常惊讶。 
<P>用另一种方法阐述这个事实:一种事物的容器不是一种任何事物的容器。也许很难接受,但这是事实。 
<P>你可以不喜欢它,但必须接受它。 
<P>我们在OO/C++训练课程使用的最后一个例子:“一袋苹果不是一袋水果”。如果一袋苹果能够被传递给一袋水果的话,就可以把香蕉放入袋中,即使它被认为里面只能放苹果! 

<P>(注意:&nbsp;本&nbsp;FAQ&nbsp;的论述仅与公有继承(<TT>public</TT> inheritance)有关;&nbsp;<A 
href="http://www.sunistudio.com/cppfaq/private-inheritance.html">私有和保护继承</A>并不相同) 

<P><SMALL>[&nbsp;<A 
href="http://www.sunistudio.com/cppfaq/proper-inheritance.html#top">Top</A> 
|&nbsp;<A 
href="http://www.sunistudio.com/cppfaq/proper-inheritance.html#bottom">Bottom</A> 
|&nbsp;<A 
href="http://www.sunistudio.com/cppfaq/virtual-functions.html">Previous&nbsp;section</A> 
|&nbsp;<A 
href="http://www.sunistudio.com/cppfaq/abcs.html">Next&nbsp;section</A> 
]</SMALL> 
<HR>

<P><A name=[21.4]></A>
<DIV class=FaqTitle>
<H3>[21.4] <TT>Derived</TT>数组是一种&nbsp;<TT>Base</TT>数组吗?<IMG alt=UPDATED! 
src="[21] Inheritance proper inheritance and substitutability, C++ FAQ Lite.files/updated.gif"></H3></DIV><SMALL><EM>[Recently 
changed so it uses new-style headers and the <TT>std::</TT> syntax and reworded 
references to STL (on 7/00). <A 
href="http://www.sunistudio.com/cppfaq/proper-inheritance.html#[21.5]">Click 
here to go to the next FAQ in the "chain" of recent changes<!--rawtext:[21.5]:rawtext--></A>.]</EM></SMALL> 
<P>不。 
<P>这是以上FAQ的结论。不幸的是它会把你带入困境,考虑一下这个: 
<P>
<DIV 
class=CodeBlock><TT>&nbsp;class&nbsp;Base&nbsp;{<BR>&nbsp;public:<BR>&nbsp;&nbsp;&nbsp;virtual&nbsp;void&nbsp;f();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TT><EM>//&nbsp;1</EM><TT><BR>&nbsp;};<BR>&nbsp;<BR>&nbsp;class&nbsp;Derived&nbsp;:&nbsp;public&nbsp;Base&nbsp;{<BR>&nbsp;public:<BR>&nbsp;&nbsp;&nbsp;</TT><EM>//&nbsp;...</EM><TT><BR>&nbsp;private:<BR>&nbsp;&nbsp;&nbsp;int&nbsp;i_;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TT><EM>//&nbsp;2</EM><TT><BR>&nbsp;};<BR>&nbsp;<BR>&nbsp;void&nbsp;userCode(Base*&nbsp;arrayOfBase)<BR>&nbsp;{<BR>&nbsp;&nbsp;&nbsp;arrayOfBase[1].f();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TT><EM>//&nbsp;3</EM><TT><BR>&nbsp;}<BR>&nbsp;<BR>&nbsp;int&nbsp;main()<BR>&nbsp;{<BR>&nbsp;&nbsp;&nbsp;Derived&nbsp;arrayOfDerived[10];&nbsp;&nbsp;&nbsp;</TT><EM>//&nbsp;4</EM><TT><BR>&nbsp;&nbsp;&nbsp;userCode(arrayOfDerived);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TT><EM>//&nbsp;5</EM><TT><BR>&nbsp;} </TT></DIV>
<P>编译器会认为这是完美的类型安全。编号&nbsp;5的这一行将&nbsp;<TT>Derived*</TT>&nbsp;转换为&nbsp;<TT>Base*</TT>。但实际上这样做是可怕的:由于&nbsp;<TT>Derived</TT>比<TT>Base</TT>&nbsp;大,在编号3的这一行的指针运算是错误的:当编译器计算&nbsp;<TT>arrayOfBase[1]</TT>的地址时使用&nbsp;<TT>sizeof(Base)</TT>,而数组其实是一个<TT>Derived</TT>数组,这意味着在编号3的这一行的所计算的地址(以及之后的成员函数 
<TT>f() </TT>的调用)并不在任何对象的起始位置!而在<TT>Derived</TT>对象的中间。假设你的编译器使用通常的方法寻找<A 
href="http://www.sunistudio.com/cppfaq/virtual-functions.html">虚函数</A>,那么将导致第一个<TT>Derived</TT>对象的&nbsp;<TT>int&nbsp;i_</TT>&nbsp;被重新解释,将它看作指向虚函数表的指针,跟随着这个“指针”(意味着我们正在访问一个随机的内存位置),并将内存中那个位置的前几个字节解释为&nbsp;C++成员函数的地址,然后将它们(随机的内存地址)装载到指令寄存器并开始从那个内存区产生机器指令。发生这样情况的几率相当高。
<P>根本问题是&nbsp;C++无法区别指向事物的指针和指向事物数组的指针。自然的,C++是从C继承了这一特征。
<P>注意:如果我们使用类似数组(array-like)的类(例如,<A 
href="http://www.sunistudio.com/cppfaq/class-libraries.html#[32.1]">标准库</A>中的<TT>std::vector&lt;Derived&gt;</TT>)来代替原始的数组,这个问题将会被作为编译时错误找出而不是运行时的灾难。 

<P>(注意:&nbsp;本&nbsp;FAQ&nbsp;的论述仅与公有继承(<TT>public</TT> inheritance)有关;&nbsp;<A 
href="http://www.sunistudio.com/cppfaq/private-inheritance.html">私有和保护继承</A>并不相同) 

<P><SMALL>[&nbsp;<A 
href="http://www.sunistudio.com/cppfaq/proper-inheritance.html#top">Top</A> 
|&nbsp;<A 
href="http://www.sunistudio.com/cppfaq/proper-inheritance.html#bottom">Bottom</A> 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -