📄 00000001.htm
字号:
<BR>第二个方案中,可让卵形 "Oval" 类别有个 "setSize(size)":将 "width()" 和 <BR>"height()" 都设成 "size",然後让椭圆和圆形都自卵形中衍生出来。椭圆(而不是 <BR>正圆形)会加入一个 "setSize(x,y)" 运算(如果这个 "setSize()" 运作行为的名 <BR>称重复了,就得注意前面提过的「遮蔽效应」)。 <BR> <BR>======================================== <BR> <BR>Q62:对「圆形是/不是一种椭圆」这两难问题,有没有其他说法? <BR> <BR>如果你说:椭圆都可以不对称地挤压,又说:圆形是一种椭圆,又说:圆形不能不对 <BR>称地挤压下去,那麽很明显的,你说过的某句话要做修正(老实说,该取消掉)。所 <BR>以你不是得去掉 "Ellipse::setSize(x,y)",去掉圆形和椭圆间的继承关系,就是得 <BR>承认你的「圆形」不一定是正圆。 <BR> <BR>这儿有两个 OO/C++ 新手最易落入的陷阱。他们想用程式小技巧来弥补差劲的事前设 <BR>计(他们重新定义 Circle::setSize(x,y),让它丢出一个例外,呼叫 "abort()" , <BR>或是选用两参数的平均数,或是不做任何事情),不幸的,这些技俩都会让使用者感 <BR>到吃惊:他们原本都预期 "width() == x" 和 "height() == y" 这两个事实会成立。 <BR> <BR>唯一合理的做法似乎是:降低椭圆形 "setSize(x,y)" 的保证事项(譬如,你可以改 <BR>成:「这运作行为“可能”会把 width() 设成 x、height() 设成 y,也可能“不做 <BR>任何事”」)。不幸的,这样会把界限冲淡,因为使用者没有任何有意义的物件行为 <BR>足以依靠,整个类别阶层也就无毫价值可言了(很难说服别人去用一个:问你说它是 <BR>做什麽的,你却只会耸耸肩膀说不知道的物件)。 <BR> <BR>========================== <BR>● 12C:继承--存取规则 <BR>========================== <BR> <BR>Q63:为什麽衍生的类别无法存取基底的 "private" 东西? <BR> <BR>让你不被基底类别将来的改变所影响。 <BR> <BR>衍生类别不能存取到基底的私有(private)成员,它有效地把衍生类别「封住」, <BR>基底类别内的私有成员如有改变,也不会影响到衍生的类别。 <BR> <BR>======================================== <BR> <BR>Q64:"public:"、"private:"、"protected:" 的差别是? <BR> <BR>"Private:" 在前几节中讨论过了;"public:" 是指:「任何人都能存取之」;第三 <BR>个 "protected:" 是让某成员(资料成员或是成员函数)只能由衍生类别存取之。 <BR> <BR>【译注】"protected:" 是让「衍生类别」,而非让「衍生类别的物件案例」能存取 <BR> 得到 protected 的部份。 <BR> <BR>======================================== <BR> <BR>Q65:当我改变了内部的东西,怎样避免子类别被破坏? <BR> <BR>物件类别有两个不同的介面,提供给不同种类的用户: <BR> * "public:" 介面用以服务不相关的类别。 <BR> * "protected:" 介面用以服务衍生的类别。 <BR> <BR>除非你预期所有的子类别都会由你们的工作小组建出来,否则你应该将基底类别的资 <BR>料位元内容放在 "private:" 处,用 "protected:" 行内存取函数来存取那些资料。 <BR>这样的话,即使基底类别的私有资料改变了,衍生类别的程式也不会报废,除非你改 <BR>变了基底类别的 protected 处的存取函数。 <BR> <BR>================================ <BR>● 12D:继承--建构子与解构子 <BR>================================ <BR> <BR>Q66:若基底类别的建构子呼叫一个虚拟函数,为什麽衍生类别覆盖掉的那个虚拟函 <BR> 数却不会被呼叫到? <BR> <BR>在基底类别 Base 的建构子执行过程中,该物件还不是属於衍生 Derived 的,所以 <BR>如果 "Base::Base()" 呼叫了虚拟函数 "virt()",则 "Base::virt()" 会被呼叫, <BR>即使真的有 "Derived::virt()"。 <BR> <BR>类似的道理,当 Base 的解构子执行时,该物件不再是个 Derived 了,所以当 <BR>Base::~Base() 呼叫 "virt()",则 "Base::virt()" 会被执行,而非覆盖後的版本 <BR>"Derived::virt()"。 <BR> <BR>当你想像到:如果 "Derived::virt()" 碰得到 Derived 类别的物件成员,会造成什 <BR>麽样的灾难,你很快就会看出这规则的明智之处。 <BR> <BR>================================ <BR> <BR>Q67:衍生类别的解构子应该外显地呼叫基底的解构子吗? <BR> <BR>不要,绝对不要外显地呼叫解构子(「绝对不要」指的是「几乎完全不要」)。 <BR> <BR>衍生类别的解构子(不管你是否明显定义过)会“自动”去呼叫成员物件的、以及基 <BR>底类别之子物件的解构子。成员物件会以它们在类别中出现的相反顺序解构,接下来 <BR>是基底类别的子物件,以它们出现在类别基底列表的相反顺序解构之。 <BR> <BR>只有在极为特殊的情况下,你才应外显地呼叫解构子,像是:解构一个由「新放入的 <BR>new 运算子」配置的物件。 <BR> <BR>=========================================== <BR>● 12E:继承--Private 与 protected 继承 <BR>=========================================== <BR> <BR>Q68:该怎麽表达出「私有继承」(private inheritance)? <BR> <BR>用 ": private" 来代替 ": public." 譬如: <BR> <BR> class Foo : private Bar { <BR> //... <BR> }; <BR> <BR>================================ <BR> <BR>Q69:「私有继承」和「成份」(composition) 有多类似? <BR> <BR>私有继承是「成份」(has-a) 的一种语法变形。 <BR> <BR>譬如:「汽车有引擎」("car has-a engine") 关系可用成份来表达: <BR> <BR> class Engine { <BR> public: <BR> Engine(int numCylinders); <BR> void start(); //starts this Engine <BR> }; <BR> <BR> class Car { <BR> public: <BR> Car() : e_(8) { } //initializes this Car with 8 cylinders <BR> void start() { e_.start(); } //start this Car by starting its engine <BR> private: <BR> Engine e_; <BR> }; <BR> <BR>同样的 "has-a" 关系也可用私有继承来表达: <BR> <BR> class Car : private Engine { <BR> public: <BR> Car() : Engine(8) { } //initializes this Car with 8 cylinders <BR> Engine::start; //start this Car by starting its engine <BR> }; <BR> <BR>这两种型式的成份有几分相似性: <BR> * 这两种情况之下,Car 只含有一个 Engine 成员物件。 <BR> * 两种情况都不能让(外界)使用者由 Car* 转换成 Engine* 。 <BR> <BR>也有几个不同点: <BR> * 如果你想要让每个 Car 都含有数个 Engine 的话,就得用第一个型式。 <BR> * 第二个型式可能会导致不必要的多重继承(multiple inheritance)。 <BR> * 第二个型式允许 Car 的成员从 Car* 转换成 Engine* 。 <BR> * 第二个型式可存取到基底类别的 "protected" 成员。 <BR> * 第二个型式允许 Car 覆盖掉 Engine 的虚拟函数。 <BR> <BR>注意:私有继承通常是用来获得基底类别 "protected:" 成员的存取权力,但这通常 <BR>只是个短程的解决方案。 <BR> <BR>======================================== <BR> <BR>Q70:我比较该用哪一种:成份还是私有继承? <BR> <BR>成份。 <BR> <BR>正常情形下,你不希望存取到太多其他类别的内部,但私有继承会给你这些额外的权 <BR>力(与责任)。不过私有继承不是洪水猛兽;它只是得多花心力去维护罢了,因为它 <BR>增加了别人动到你的东西、让你的程式出差错的机会。 <BR> <BR>合法而长程地使用私有继承的时机是:当你想新建一个 Fred 类别,它会用到 Wilma <BR>类别的程式码,而且 Wilma 的程式码也会呼叫到你这个 Fred 类别里的运作行为时 <BR>。这种情形之下,Fred 呼叫了 Wilma 的非虚拟函数,Wilma 也呼叫了它自己的、会 <BR>被 Fred 所覆盖的虚拟函数(通常是纯虚拟函数)。要用成份来做的话,太难了。 <BR> <BR> class Wilma { <BR> protected: <BR> void fredCallsWilma() <BR> { cout << "Wilma::fredCallsWilma()\n"; wilmaCallsFred(); } <BR> virtual void wilmaCallsFred() = 0; <BR> }; <BR> <BR> class Fred : private Wilma { <BR> public: <BR> void barney() <BR> { cout << "Fred::barney()\n"; Wilma::fredCallsWilma(); } <BR> protected: <BR> virtual void wilmaCallsFred() <BR> { cout << "Fred::wilmaCallsFred()\n"; } <BR> }; <BR> <BR>======================================== <BR> <BR>Q71:我应该用指标转型方法,把「私有」衍生类别转成它的基底吗? <BR> <BR>当然不该。 <BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -