分享
 
 
 

转贴:C++语言常见问题解:#94 ~ #104

王朝c/c++·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

这是我从台湾的http://www.cis.nctu.edu.tw/chinese/doc/research/c++/C++FAQ-Chinese/发现的《C++ Frequently Asked Questions》的繁体翻译,作者是:叶秉哲,也是《C++ Programming Language》3/e繁体版的译者,该文章是非常的好,出于学习用途而将它转贴,本人未取得作者的授权,原文章的版权仍然归属原作者.

C++语言常见问题解

Q94:Smalltalk/C++ 不同的继承,在现实里导致的结果是什么?

Smalltalk 让你做出不是子类别的子型别,做出不是子型别的子类别,它可让

Smalltalk 程序者不必操心该把哪种资料(位﹑表现型式﹑数据结构)放进类别里

面(譬如,你可能会把连结串行放到堆栈类别里)。毕竟,如果有人想要个以数组做

出的堆栈,他不必真的从堆栈继承过来;喜欢的话,他可以从数组类别 Array 中继

承过来,即使 ArrayBasedStack 并“不是”一种数组!)

在 C++ 中,你不可能不为此操心。只有机制(运作行为的程序代码),而非表现法(

资料位)可在子类别中被覆盖掉,所以,通常你“不要”把数据结构放进类别里比

较好。这会促成 Abstract Base Classes (ABCs) 的强烈使用需求。

我喜欢用 ATV 和 Maseratti 之间的差别来比喻。ATV(all terrain vehicle,越野

车)很好玩,因为你可以「到处逛」,任意开到田野﹑小溪﹑人行道等地。另一方面

,Maseratti 让你能高速行驶,但你只能在公路上面开。就算你喜欢「自由表现力」

,偏偏喜欢驶向丛林,但也请不要在 C++ 里这么做;它不适合。

========================================

Q95:学过「纯种」的 OOPL 之后才能学 C++ 吗?

不是(事实上,这样可能反而会害了你)。

(注意:Smalltalk 是个「纯种」的 OOPL,而 C++ 是个「混血」的 OOPL。)读这

之前,请先读读前面关于 C++ 与 Smalltalk 差别的 FAQs。

OOPL 的「纯粹性」,并不会让转移到 C++ 更容易些。事实上,典型的动态系结与非

子型别的继承,会让 Smalltalk 程序者更难学会 C++。Paradigm Shift 公司曾教过

数千人 OO 技术,我们注意到:有 Smalltalk 背景的人来学 C++,通常和那些根本

没碰过继承的人学起来差不多累。事实上,对动态型别的 OOPL(通常是,但不全都

是 Smalltalk)有高度使用经验的人,可能会“更难”学好,因为想把过去的习惯“

遗忘”,会比一开始就学习静态型别来得困难。

【译注】作者是以「语言学习」的角度来看的。事实上,若先有 Smalltalk 之类的

对象导向观念的背景知识,再来学 C++ 就不必再转换 "paradigm"--对象

导向的中心思维是不会变的,变的只是实行细节而已。

========================================

Q96:什么是 NIHCL?到哪里拿到它?

NIHCL 代表 "national-institute-of-health's-class-library",美国国家卫生局

对象链接库。取得法:anonymous ftp 到 [128.231.128.7],

档案:pub/nihcl-3.0.tar.Z 。

NIHCL(有人念作 "N-I-H-C-L",有人念作 "nickel")是个由 Smalltalk 转移过来

的 C++ 对象链接库。有些 NIHCL 用到的动态型别很棒(譬如:persistent objects

,持续性对象),也有些地方动态型别会和 C++ 语言的静态型别相冲突,造成紧张

关系。

详见前面关于 Smalltalk 的 FAQs。

===============================

■□ 第16节:参考与数值语意

===============================

Q97:什么是数值以及参考语意?哪一种在 C++ 里最好?

在参考语意 (reference semantics) 中,「设定」是个「指针拷贝」的动作(也就

是“参考”这个词的本意),数值语意 (value semantics,或 "copy" semantics)

的设定则是真正地「拷贝其值」,而不是做指针拷贝的动作。C++ 让你选择:用设定

运操作数来拷贝其值(copy/value 语意),或是用指针拷贝方式来拷贝指针

(reference 语意)。C++ 让你能覆盖掉 (override) 设定运操作数,让它去做你想要

的事,不过系统预设的(而且是最常见的)方式是拷贝其「数值」。

参考语意的优点:弹性﹑动态系结(在 C++ 里,你只能以传指针或传参考来达到动

态系结,而不是用传值的方式)。

数值语意的优点:速度。对需要对象(而非指针)的场合来说,「速度」似乎是很奇

怪的特点,但事实上,我们比较常存取对象本身,较不常去拷贝它。所以偶尔的拷贝

所付出的代价,(通常)会被拥有「真正的对象本身」﹑而非仅是指向对象的指针所

带来的效益弥补过去。

有三个情况,你会得到真正的对象,而不是指向它的指针:区域变量﹑整体/静态变

数﹑完全被某类别包含在内 (fully contained) 的成员对象。这里头最重要的就是

最后一个(也就是「成份」)。

后面的 FAQs 会有更多关于 copy-vs-reference 语意的信息,请全部读完,以得到

较平衡的观点。前几则会刻意偏向数值语意,所以若你只读前面的,你的观点就会有

所偏颇。

设定 (assignment) 还有别的事项(譬如:shallow vs deep copy)没在这儿提到。

========================================

Q98:「虚拟数据」是什么?怎么样/为什么该在 C++ 里使用它?

虚拟资料让衍生类别能改变基底类别的对象成员所属的类别。严格说来,C++ 并不「

支持」虚拟数据,但可以仿真出来。不漂亮,但还能用。

欲仿真之,基底类别必须有个指针指向成员对象,衍生类别必须提供一个 "new" 到

的对象,以让原基底类别的指针所指到。该基底类别也要有一个以上正常的建构子,

以提供它们自己的参考(也是透过 "new"),且基底类别的解构子也要 "delete" 掉

被参考者。

举例来说,"Stack" 类别可能有个 Array 成员对象(采用指针),衍生类别

"StretchableStack" 可能会把基底类别的成员资料 "Array" 覆盖成

"StretchableArray"。想做到的话,StretchableArray 必须继承自 Array,这样子

Stack 就会有个 "Array*"。Stack 的正常建构子会用 "new Array" 来初始化它的

"Array*",但 Stack 也会有一个(可能是在 "protected:" 里)特别的建构子,以

自衍生类别中接收一个 "Array*"; StretchableArray 的建构子会用

"new StretchableArray" 把它传给那个特别的建构子。

优点:

* 容易做出 StretchableStack(大部份的程序都继承下来了)。

* 使用者可把 StretchableStack 当成“是一种”Stack 来传递。

缺点:

* 多增加额外的间接存取层,才能碰到 Array。

* 多增加额外的自由内存配置负担(new 与 delete)。

* 多增加额外的动态系结负担(原因请见下一则 FAQ)。

换句话说,在“我们”这一边,很轻松就成功做出 StretchableStack,但所有用户

却都为此付出代价。不幸的,额外负荷不仅在 StretchableStack 会有,连 Stack

也会。

请看下下一则 FAQ,看看使用者会「付出」多少代价。也请读读下一则 FAQ 以后的

几则(不看其它的,你将得不到平衡的报导)。

========================================

Q99:虚拟数据和动态数据有何差别?

最容易分辨出来的方法,就是看看颇为类似的「虚拟函数」。虚拟成员函数是指:在

所有子类别中,它的宣告(型态签名)部份必须保持不变,但是定义(本体)的部份

可以被覆盖(override)。继承下来的成员函数可被覆盖,是子类别的静态性质

(static property);它不随任何对象之生命期而动态地改变,同一个子类别的不同

对象,也不可能会有不同的成员函数的定义。

现在请回头重读前面这一段,但稍作些代换:

* 「成员函数」 --> 「成员对象」

* 「型态签名」 --> 「型别」

* 「本体」 --> 「真正所属的类别」

这样子,你就看到虚拟数据的定义。

从另一个角度来看,就是把「各个对象」的成员函数与「动态」成员函数区分开来。

「各个对象」成员函数是指:在任何对象案例中,该成员函数可能会有所不同,可能

会塞入函数指针来实作出来;这个指针可以是 "const",因为它在对象生命期中不会

变更。「动态」成员函数是指:该成员函数会随时间动态地改变;也可能以函数指针

来做,但该指针不会是 const 的。

推而广之,我们得到三种不同的资料成员概念:

* 虚拟数据:成员对象的定义(真正所属的类别)可被子类别覆盖,只要它的宣告

(型别)维持不变,且此覆盖是子类别的静态性质。

* 各对象的资料:任何类别的对象在初始化时,可以案例化不同型式(型别)的成

员对象(通常是一个 "wrapper" 包起来的对象),且该成员对象真正所属的类别

,是把它包起来的那个对象之静态性质。

* 动态数据:成员对象真正所属的类别,可随时间动态地改变。

它们看起来都差不多,是因为 C++ 都不「直接支持」它们,只是「能做得出来」而

已;在这种情形下,仿真它们的机制也都一样:指向(可能是抽象的)基底类别的指

标。在直接提供这些 "first class" 抽象化机制的语言中,这三者间的差别十分明

显,它们各有不同的语法。

========================================

Q100:我该正常地用指针来配置资料成员,还是该用「成份」(composition)?

成份。

正常情况下,你的成员资料应该被「包含」在合成的对象里(但也不总是如此;

"wrapper" 对象就是你会想用指针/参考的好例子;N-to-1-uses-a 的关系也需要某

种指针/参考之类的东西)。

有三个理由说明,完全被包含的成员对象(「成份」)的效率,比自由配置对象的指

标还要好:

* 额外的间接层,每当你想存取成员对象时。

* 额外的动态配置("new" 于建构子中,"delete" 于解构子中)。

* 额外的动态系结(底下会解释)。

========================================

Q101:动态配置成员对象有三个效率因素,它们的相对代价是多少?

这三个效率因素,上一则 FAQ 有列举出来:

* 以它本身而言,额外的间接层影响不大。

* 动态配置可能是个效率问题(当有许多配置动作时,典型的 malloc 会拖慢速度

;OO 软件会被动态配置拖垮,除非你事先就留意到它了)。

* 用指针而非对象的话,会带来额外的动态系结。每当 C++ 编译器能知道某对象「

真正的」类别,该虚拟函数呼叫就能“静态”地系结住,能够被 inline。Inline

可能有无限大的 (但你可能只会相信有半打的 :-) 最佳化机会,像是 procedural

integration﹑缓存器生命期等等事项。三种情形之下,C++ 编译器能知道对象真

正的类别:区域变量﹑整体/静态变量﹑完全被包含的成员对象。

完全被包含的成员对象,可达到很大的最佳化效果,这是「成员对象的指针」所不可

能办到的。这也就是为什么采用参考语意的语言,会「与生俱来」就效率不彰的原因

了。

注意:请读读下面三则 FAQs 以得到平衡的观点!

========================================

Q102:"inline virtual" 的成员函数真的会被 "inline" 吗?

Yes,可是...

一个透过指针或参考的 virtual 呼叫总是动态决定的,可能永远都不会被 inline。

原因:编译器直到执行时(亦即:动态地),才会知道该呼叫哪个程序,因为那一段

程序,可能会来自呼叫者编译过后才出现的衍生类别。

因此,inline virtual 的呼叫可被 inline 的唯一时机是:编译器有办法知道某物

件「真正所属的类别」之时,这是虚拟函数呼叫里所要知道的事情。这只会发生在:

编译器拥有真正的对象,而非该对象的指针或参考。也就是说:不是区域变量﹑整体

/静态对象,就是合成对象里的完全包含对象。

注意:inline 和非 inline 的差距,通常会比正常的和虚拟的函数呼叫之差别更为

显著。譬如,正常的与虚拟的函数呼叫,通常只差两个内存参考的动作而已,可是

inline 与非 inline 函数就会有一个数量级的差距(与数万次影响不大的成员函数

呼叫相比,函数没有用 inline virtual 的话,会造成 25X 的效率损失!

[Doug Lea, "Customization in C++," proc Usenix C++ 1990]).

针对此现象的对策:不要陷入编译器/语言厂商之间,对彼此产品的虚拟函数呼叫,

做永无止尽的性能比较争论(或是广告噱头!)之中。和语言/编译器能否将成员函

数呼叫做「行内展开」相比,这种比较完全没有意义。也就是说,许多语言编译器厂

商,拼命强调他们的函数分派方式有多好,但如果他们没做“行内”成员函数呼叫的

话,整体性能还是会很差,因为 inline--而非分派--才是最重要的性能影响因

素。

注意:请读读下两则 FAQs 以看看另一种说法!

========================================

Q103:看起来我不应该用参考语意了,是吗?

错。

参考语意是个好东西。我们不能拋弃指针,我们只要不让软件的指针变成一个大老鼠

窝就行了。在 C++ 里,你可以选择该用参考语意(指针/参考)还是数值语意(物

件真正包含其它对象)的时机。在大型系统中,两者应该取得平衡。然而如果你全都

用指针来做的话,速度会大大的降低。

接近问题层次的对象,会比较高阶的对象还要大。这些针对「问题空间」抽象化的个

体本身,通常比它们内部的「数值」更为重要。参考语意应该用于问题空间的对象上

注意:问题空间的对象,通常会比解题空间的更为高阶抽象化,所以相对地问题空间

的对象通常会有较少的交谈性。因此 C++ 给我们一个“理想的”解决法:我们用参

考语意,来对付那些需要独立的个体识别 (identity) 者,或是大到不适合直接拷贝

的对象;其它情形则可选择数值语意。因此,使用频率较高的就用数值语意,因为(

只有)在不造成伤害的场合下,我们才去增加弹性;必要时,我们还是选择效率!

还有其它关于实际 OO 设计方面的问题。想精通 OO/C++ 得花时间,以及高素质的训

练。若你想有个强大的工具,你必须投资下去。

<<<< 还不要停下来! 请一并读读下一则 FAQ!! >>>>

========================================

Q104:参考语意效率不高,那么我是否应该用传值呼叫?

不。

前面的 FAQ 是讨论“成员对象”(member object) 的,而不是函数参数。一般说来

,位于继承阶层里的对象,应该用参考或指针来传递,而非传值,因为惟有如此你才

能得到(你想要的)动态系结(传值呼叫和继承不能安全混用,因为如果把大大的子

类别对象当成基底的对象来传值的话,它会被“切掉”)。

除非有足以令人信服的反方理由,否则成员对象应该用数值,而参数该用参考传递。

前几则 FAQs 提到一些「足以信服的理由」,以支持“成员对象该用参考”一事了。

--

Marshall Cline

--

Marshall P. Cline, Ph.D. / Paradigm Shift Inc / PO Box 5108 / Potsdam NY 13676

cline@sun.soe.clarkson.edu / 315-353-6100 / FAX: 315-353-6110

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有