📄 00000002.htm
字号:
<BR>这可不是对 Smalltalk 恐怖份子挑□,让他们趁我熟睡时戳我的轮胎(在我很难得 <BR>有空休息的这段时间内 :-) 。 <BR> <BR>======================================== <BR> <BR>Q89:C++ 和 Smalltalk 的差别在哪? <BR> <BR>最重要的不同是: <BR> <BR> * 静态型别或动态型别? <BR> * 继承只能用於产生子型别上? <BR> * 数值语意还是参考语意 (value vs reference semantics)? <BR> <BR>头两个差异会在这一节中解释,第三点则是下一节的讨论主题。 <BR> <BR>如果你是 Smalltalk 程式者,现在想学 C++,底下三则 FAQs 最好仔细研读。 <BR> <BR>======================================== <BR> <BR>Q90:什麽是「静态型别」?它和 Smalltalk 有多相似/不像? <BR> <BR>静态型别(static typing)是说:编译器会“静态地”(於编译时期)检验各运算 <BR>的型态安全性,而不是产生执行时才会去检查的程式码。例如,在静态型别之下,会 <BR>去侦测比对函数引数的型态签名,不正确的配对会被编译器挑出错误来,而非在执行 <BR>时才被挑出。 <BR> <BR>OO 的程式里,最常见的「型态不符」错误是:欲对某物件启动个成员函数,但该物 <BR>件并未准备好要处理该运算动作。譬如,如果 "Fred" 类别有成员函数 "f()" 但没 <BR>有 "g()",且 "fred" 是 "Fred" 类别的案例,那麽 "fred.f()" 就是合法的, <BR>"fred.g()" 则是非法的。C++(静态地)在编译期捕捉型别错误,Smalltalk 则(动 <BR>态地)在执行期捕捉。(技术上,C++ 很像 Pascal--“半”静态型别--因为指 <BR>标转型与 union 都能用来破坏型别系统;这提醒了我们:你用指标转型与 union 的 <BR>频率,只能像你用 "goto" 那样。) <BR> <BR>======================================== <BR> <BR>Q91:「静态型别」与「动态型别」哪一种比较适合 C++? <BR> <BR>若你想最有效率使用 C++,请把她当成静态型别语言来用。 <BR> <BR>C++ 极富弹性,你可以(藉由指标转型、union 或 #define)让她「长得」像 <BR>Smalltalk。但是不要这样做。这提醒了我们:少用 #define。 <BR> <BR>有些场合,指标转型和 union 是必要的,甚至是很好的做法,但须谨慎为之。指标 <BR>转型等於是叫编译器完全信赖你。错误的指标转型可能会毁坏堆积、在别的物件记忆 <BR>体中乱搞、呼叫不存在的运作行为、造成一般性错误(general failure)。这是很 <BR>糟糕的事。如果你避免用与这些相关的东西,你的 C++ 程式会更安全、更快,因为 <BR>能在编译期就检测的东西,就不必留到执行期再做。 <BR> <BR>就算你喜欢动态型别,也请避免在 C++ 里使用,或者请考虑换另一个将型态检查延 <BR>迟到执行期才做的语言。C++ 将型态检验 100% 都放在编译时期;她没有任何执行期 <BR>型态检验的内建机制。如果你把 C++ 当成一个动态型别的 OOPL 来用,你的命运将 <BR>操之汝手。 <BR> <BR>======================================== <BR> <BR>Q92:怎样分辨某个 C++ 物件程式库是否属於动态型别的? <BR> <BR>提示 #1:当所有东西都衍生自单一的根类别(root class),通常叫做 "Object"。 <BR>提示 #2:当容器类别 container classes,像 List、Stack、Set 等,都不是 <BR> template 版的。 <BR>提示 #3:当容器类别(List、Stack、Set 等)把插入/取出的元素,都视为指向 <BR> "Object" 的指标时。(你可以把 Apple 放进容器中,但当你取出时,编 <BR> 译器只知道它是衍生自 Object,所以你得用指标转型将它转回 Apple* ; <BR> 你最好祈祷它真的是个 Apple,否则你会脑充血的。) <BR> <BR>你可用 "dynamic_cast"(於 1994 年才加入的)来使指标转型「安全些」,但这种 <BR>动态测试依旧是“动态”的。这种程式风格是 C++ 动态型别的基本要素,你可以呼 <BR>叫函数:「把这个 Object 转换成 Apple,或是给我个 NULL,如果它不是 Apple的 <BR>话」,你就得到动态型别了:直到执行时期才知道会发生什麽事。 <BR> <BR>若你用 template 去实作出容器类别,C++ 编译器会静态侦测出 99% 的型态资讯( <BR>"99%" 并不是真的;有些人宣称能做到 100%,而那些需要持续性 (persistence) 的 <BR>人,只能得到低於 100% 的静态型别检验)。重点是:C++ 透过 template 来做到泛 <BR>型(genericity),而非透过继承。 <BR> <BR>======================================== <BR> <BR>Q93:在 C++ 里怎样用继承?它和 Smalltalk 有何不同? <BR> <BR>有些人认为继承是用来重用程式码的。在 C++ 中,这是不对的。说明白点,「继承 <BR>不是『为』重用程式码而设计的。」 <BR> <BR>【译注】这一个分野相当重要。否则,C++ 使用者就会感染「继承发烧症」 <BR> (inheritance fever)。 <BR> <BR>C++ 继承的目的是用来表现介面一致性(产生子类别),而不是重用程式码。C++ 中 <BR>,重用程式码通常是靠「成份」(composition) 而非继承。换句话说,继承主要是用 <BR>来当作「特异化」(specialization) 的技术,而非实作上的技巧。 <BR> <BR>这是与 Smalltalk 主要的不同之处,在 Smalltalk 里只有一种继承的型式(C++ 有 <BR>"private" 继承--「共享程式码,但不承袭其介面」,有 "public" 继承--表现 <BR>"kind-of" 关系)。Smalltalk 语言非常(相对於只是程式的习惯)允许你置放一个 <BR>override 覆盖(它会去呼叫个「我看不懂」的运作行为),以达到「隐藏住」继承 <BR>下来的运作行为的“效果”。更进一步,Smalltalk 可让观念界的 "is-a" 关系“独 <BR>立於”子类别阶层之外(子型别不必也是子类别;譬如,你可以让某个东西是一个 <BR>Stack,却不必继承自 Stack 类别)。 <BR> <BR>相反的,C++ 对继承的限制更严:没办法不用到继承就做出“观念上的 is-a”关系 <BR>(有个 C++ 的解决方法:透过 ABC 来分离介面与实作)。C++ 编译器利用公共继承 <BR>额外附的语意资讯,以提供静态型别。 <BR> <BR>======================================== <BR> <BR>Q94:Smalltalk/C++ 不同的继承,在现实里导致的结果是什麽? <BR> <BR>Smalltalk 让你做出不是子类别的子型别,做出不是子型别的子类别,它可让 <BR>Smalltalk 程式者不必操心该把哪种资料(位元、表现型式、资料结构)放进类别里 <BR>面(譬如,你可能会把连结串列放到堆叠类别里)。毕竟,如果有人想要个以阵列做 <BR>出的堆叠,他不必真的从堆叠继承过来;喜欢的话,他可以从阵列类别 Array 中继 <BR>承过来,即使 ArrayBasedStack 并“不是”一种阵列!) <BR> <BR>在 C++ 中,你不可能不为此操心。只有机制(运作行为的程式码),而非表现法( <BR>资料位元)可在子类别中被覆盖掉,所以,通常你“不要”把资料结构放进类别里比 <BR>较好。这会促成 Abstract Base Classes (ABCs) 的强烈使用需求。 <BR> <BR>我喜欢用 ATV 和 Maseratti 之间的差别来比喻。ATV(all terrain vehicle,越野 <BR>车)很好玩,因为你可以「到处逛」,任意开到田野、小溪、人行道等地。另一方面 <BR>,Maseratti 让你能高速行驶,但你只能在公路上面开。就算你喜欢「自由表现力」 <BR>,偏偏喜欢驶向丛林,但也请不要在 C++ 里这麽做;它不适合。 <BR> <BR>======================================== <BR> <BR>Q95:学过「纯种」的 OOPL 之後才能学 C++ 吗? <BR> <BR>不是(事实上,这样可能反而会害了你)。 <BR> <BR>(注意:Smalltalk 是个「纯种」的 OOPL,而 C++ 是个「混血」的 OOPL。)读这 <BR>之前,请先读读前面关於 C++ 与 Smalltalk 差别的 FAQs。 <BR> <BR>OOPL 的「纯粹性」,并不会让转移到 C++ 更容易些。事实上,典型的动态系结与非 <BR>子型别的继承,会让 Smalltalk 程式者更难学会 C++。Paradigm Shift 公司曾教过 <BR>数千人 OO 技术,我们注意到:有 Smalltalk 背景的人来学 C++,通常和那些根本 <BR>没碰过继承的人学起来差不多累。事实上,对动态型别的 OOPL(通常是,但不全都 <BR>是 Smalltalk)有高度使用经验的人,可能会“更难”学好,因为想把过去的习惯“ <BR>遗忘”,会比一开始就学习静态型别来得困难。 <BR> <BR>【译注】作者是以「语言学习」的角度来看的。事实上,若先有 Smalltalk 之类的 <BR> 物件导向观念的背景知识,再来学 C++ 就不必再转换 "paradigm"--物件 <BR> 导向的中心思维是不会变的,变的只是实行细节而已。 <BR> <BR>======================================== <BR> <BR>Q96:什麽是 NIHCL?到哪里拿到它? <BR> <BR>NIHCL 代表 "national-institute-of-health's-class-library",美国国家卫生局 <BR>物件程式库。取得法:anonymous ftp 到 [128.231.128.7], <BR>档案:pub/nihcl-3.0.tar.Z 。 <BR> <BR>NIHCL(有人念作 "N-I-H-C-L",有人念作 "nickel")是个由 Smalltalk 转移过来 <BR>的 C++ 物件程式库。有些 NIHCL 用到的动态型别很棒(譬如:persistent objects <BR>,持续性物件),也有些地方动态型别会和 C++ 语言的静态型别相冲突,造成紧张 <BR>关系。 <BR> <BR>详见前面关於 Smalltalk 的 FAQs。 <BR> <BR> <BR>=============================== <BR>■□ 第16节:参考与数值语意 <BR>=============================== <BR> <BR>Q97:什麽是数值以及参考语意?哪一种在 C++ 里最好? <BR> <BR>在参考语意 (reference semantics) 中,「设定」是个「指标拷贝」的动作(也就 <BR>是“参考”这个词的本意),数值语意 (value semantics,或 "copy" semantics) <BR>的设定则是真正地「拷贝其值」,而不是做指标拷贝的动作。C++ 让你选择:用设定 <BR>运算子来拷贝其值(copy/value 语意),或是用指标拷贝方式来拷贝指标 <BR>(reference 语意)。C++ 让你能覆盖掉 (override) 设定运算子,让它去做你想要 <BR>的事,不过系统预设的(而且是最常见的)方式是拷贝其「数值」。 <BR> <BR>参考语意的优点:弹性、动态系结(在 C++ 里,你只能以传指标或传参考来达到动 <BR>态系结,而不是用传值的方式)。 <BR> <BR>数值语意的优点:速度。对需要物件(而非指标)的场合来说,「速度」似乎是很奇 <BR>怪的特点,但事实上,我们比较常存取物件本身,较不常去拷贝它。所以偶尔的拷贝 <BR>所付出的代价,(通常)会被拥有「真正的物件本身」、而非仅是指向物件的指标所 <BR>带来的效益弥补过去。 <BR> <BR>有三个情况,你会得到真正的物件,而不是指向它的指标:区域变数、整体/静态变 <BR>数、完全被某类别包含在内 (fully contained) 的成员物件。这里头最重要的就是 <BR>最後一个(也就是「成份」)。 <BR> <BR>後面的 FAQs 会有更多关於 copy-vs-reference 语意的资讯,请全部读完,以得到 <BR>较平衡的观点。前几则会刻意偏向数值语意,所以若你只读前面的,你的观点就会有 <BR>所偏颇。 <BR> <BR>设定 (assignment) 还有别的事项(譬如:shallow vs deep copy)没在这儿提到。 <BR> <BR>======================================== <BR> <BR>Q98:「虚拟资料」是什麽?怎麽样/为什麽该在 C++ 里使用它? <BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -