分享
 
 
 

C++语言常见问题解答(3)

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

==Part3/4=========================================================

■□第14节:程式风格指导

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

Q81:有任何好的C++程式写作的标准吗?

感谢您阅读这份文件,而不是再发明自己的一套。

但是请不要在comp.lang.c++里问这问题。几乎所有软体工程师,或多或少都把这

种东西看成是「大玩具」。而且,一些想成为C++程式撰写标准的东西,是由那些

不熟悉这语言及方法论的人弄出来的,所以最後它只能成为「过去式」的标准。这种

「摆错位置」的现象,让大家对程式写作标准产生不信任感。

很明显的,在comp.lang.c++问这问题的人,是想使自己更精进,不会因自己的无

知而绊倒,然而一些回答却只是让情况更糟而已。

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

Q82:程式撰写标准是必要的吗?有它就够了吗?

程式撰写标准不会让不懂OO的人变懂;只有训练及经验才有可能。如果它有用处的

话,那就是抑制住那些琐碎无关紧要的程式片段--当大机构想把零散的程式设计组

织整合起来时,这些片段常常会出现。

但事实上你要的不光是这种标准而已。它们提供的架构让新手少去担心一些自由度,

但是系统化的方法论会比这些好看的标准做得更好。组织机构需要的是一致性的设计

与实行“哲学”,譬如:强型别或弱型别?用指标还是参考介面?streamI/O还是

stdio?C++程式该不该呼叫C的?反过来呢?ABC该怎麽用?继承该用为实作的

技巧还是特异化的技巧?该用哪一种测试策略?一一去检查吗?该不该为每个资料成

员都提供一致的"get"和"set"介面?介面该由外往内还是由内往外设计?错误状

况该用try/catch/throw还是传回值来处理?……等等。

我们需要的是详细的“设计”部份的「半标准」。我推荐一个三段式标准:训练、谘

询顾问以及程式库。训练乃提供「密集教学」,谘询顾问让OO观念深刻化,而非仅

仅是被教过而已,高品质的程式库则是提供「长程的教学」。上述三种培训都有很热

门的市场景况。(【译注】无疑的,这是指美、加地区。)接受过上述培训的组织都

有如此的忠告:「买现成的吧,不要自己硬干(Buy,Don'tBuild.)。」买程式库,

买训练课程,买开发工具,买谘询顾问。想靠自学来达到成功的工具厂商及应用/系

统厂商,都会发现成功很困难。

【译注】这一段十分具有参考价值。不过有些背景资料得提供给各位参考。别忘了:

作者是美国人,是以该地为背景,且留意一下他所服务的公司是做什麽的..

...:-)唉!国内有这麽多的专业顾问公司吗?:-<

少数人会说:程式撰写标准只是「理想」而已,但在上述的组织机构中,它仍有其必

要性。

底下的FAQs提供一些基本的指导惯例及风格。

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

Q83:我们的组织该以以往C的经验来决定程式撰写标准吗?

No!

不论你的C经验有多丰富,不论你有多高深的C能力,好的C程式员并不会让你

直接就成为好的C++程式员。从C移到C++并不仅是学习"++"的语法语意而已

,一个组织想达到OOP的境界,却未将"OO"的精神放进OOP里的话,只是自欺罢

了;会计的资产负债表会把他们的愚蠢显现出来。

C++程式撰写标准应该由C++专家来调整,不妨先在comp.lang.c++里头问问题(

但是不要用"codingstandard"这种字眼;只要这样子问:「这种技巧有何优缺点

?」)。找个能帮你避开陷阱的高手,上个训练课程,买程式库,看看「好的」程式

库是否合乎你的程式撰写标准。绝对不要光靠自己来制定标准,除非你对它已有某种

程度的掌握。没有标准总比有烂标准好,因为不恰当的「官方说法」会让不够聪明的

平民难以追随。现在C++训练课程及程式库,已有十分兴盛的市场。

再提一件事:当某个东西炙手可热时,招摇撞骗者亦随之而生;务必三思而後行。也

要问一下从某处修过课的人,因为老手不见得也是个好教员。最後,选个懂得指导别

人的从业人员,而不是个对此语言/方法论只有过时知识的全职教师。

【译注】善哉斯言!

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

Q84:我该在函数中间或是开头来宣告区域变数?

在第一次用到它的地方附近。

物件在宣告的时候就会被初始化(被建构)。如果在初始化物件的地方没有足够的资

讯,直到函数中间才有的话,你可以在开头处初始个「空值」给它,等以後再「设定

」其值;你也可以在函数中间再初始个正确的东西给它。以执行效率来说,一开始就

让它有正确的值,会比先建立它,搞一搞它,之後再重建它来得好。以像"String"

这种简单的例子来看,会有350%的速度差距。在你的系统上可能会不同;当然整个

系统可能不会降低到300+%,但是“一定”会有不必要的性能衰退现象。

常见的反驳是:「我们会替物件的每个资料提供"set"运作行为,则建构时的额外

耗费就会分散开来。」这比效能负荷更糟,因为你添加了维护的梦靥。替每个资料提

供"set"运作行为就等於对资料不设防:你把内部实作技巧都显露出来了。你隐藏

到的只有成员物件的实体“名字”而已,但你用到的List、String和float(举例

来说)型态都曝光了。通常维护会比CPU执行时间耗费的资源更多。

区域变数应该在靠近它第一次用到之处宣告。很抱歉,这和C老手的习惯不同,但

是「新的」不见得就是「不好的」。

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

Q85:哪一种原始档命名惯例最好?"foo.C"?"foo.cc"?"foo.cpp"?

如果你已有个惯例,就用它吧。如果没有,看看你的编译器,看它用的是哪一种。典

型的答案是:".C",".cc",".cpp",或".cxx"(很自然的,".C"副档名是假设该

档案系统会区分出".C"".c"大小写)。

在ParadigmShift公司,我们在Makefiles里用".C",即使是在不区分大小写的

档案系统下(在有区分的系统中,我们用一个编译器选项:「假设.c档案都是C++

的程式」;譬如:IBMCSet++用"-Tdp",ZortechC++用"-cpp",BorlandC++用

"-P",等等)。

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

Q86:哪一种标头档命名惯例最好?"foo.H"?"foo.hh"?"foo.hpp"?

如果你已有个惯例,就用它吧。如果没有,而且你的编辑器不必去区分C和C++档

案的话,只要用".h"就行了,否则就用编辑器所要的,像".H"、".hh"或是

".hpp"。

在ParadigmShift公司,我们用".h"做为C和C++的原始档案(然後,我们就

不再建那些纯粹的C标头档案)。

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

Q87:C++有没有像lint那样的指导原则?

Yes,有一些常见的例子是危险的。

但是它们都不尽然是「坏的」,因为有些情况下,再差的例子也得用上去。

*"Fred"类别的设定运算子应该传回"*this",当成是"Fred&"(以允许成串的设

定指令)。

*有任何虚拟函数的类别,都该有个虚拟解构子。

*若一个类别有{解构子,设定运算子,拷贝建构子}其一的话,通常三者也都全

部需要。

*"Fred"类别的拷贝建构子和设定运算子,都该将它们的参数加上"const":分别

是"Fred::Fred(constFred&)"和"Fred&Fred::operator=(constFred&)"。

*类别的子物件一定要用初始化串列(initializationlists)而不要用设定的方

式,因为对使用者自订类别而言,会有很大的效率差距(3x!)。

*许多设定运算子都应该先测试:「我们」是不是「他们」;譬如:

Fred&Fred::operator=(constFred&fred)

{

if(this==&fred)return*this;

//...normalassignmentduties...

return*this;

}

有时候没必要测试,但一般说来,这些情况都是:没有必要由使用者提供外显的

设定运算子的时候(相对於编译器提供的设定运算子)。

*在那些同时定义了"+="、"+"及"="的类别中,"a+=b"和"a=a+b"通常应该

做同样的事;其他类似的内建运算子亦同(譬如:a+=1和++a;p[i]和*(p+i);

等等)。这可使用二元运算子"op="之型式来强制做到;譬如:

Fredoperator+(constFred&a,constFred&b)

{

Fredans=a;

ans+=b;

returnans;

}

这样一来,有「建构性」的二元运算甚至可以不是夥伴。但常用的运算子有时可

能会更有效率地实作出来(譬如,如果"Fred"类别本来就是个"String",且

"+="必须重新配置/拷贝字串记忆体的话,一开始就知道它的最後长度,可能会

比较好)。

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

■□第15节:Smalltalk程式者学习C++之钥

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

Q88:为什麽C++的FAQ有一节讨论Smalltalk?这是用来攻击Smalltalk的吗?

世界上「主要的」两个OOPLs是C++与Smalltalk。由於这个流行的OOPL已有第

二大的使用者总数量,许多新的C++程式者是由Smalltalk背景跳过来的。这一节

会回答以下问题:

*这两个语言的差别?

*从Smalltalk跳到C++的程式者,要知道些什麽,才能精通C++?

这一节*!*不会*!*回答这些问题:

*哪一种语言「最好」?

*为什麽Smalltalk「很烂」?

*为什麽C++「很烂」?

这可不是对Smalltalk恐怖份子挑□,让他们趁我熟睡时戳我的轮胎(在我很难得

有空休息的这段时间内:-)。

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

Q89:C++和Smalltalk的差别在哪?

最重要的不同是:

*静态型别或动态型别?

*继承只能用於产生子型别上?

*数值语意还是参考语意(valuevsreferencesemantics)?

头两个差异会在这一节中解释,第三点则是下一节的讨论主题。

如果你是Smalltalk程式者,现在想学C++,底下三则FAQs最好仔细研读。

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

Q90:什麽是「静态型别」?它和Smalltalk有多相似/不像?

静态型别(statictyping)是说:编译器会“静态地”(於编译时期)检验各运算

的型态安全性,而不是产生执行时才会去检查的程式码。例如,在静态型别之下,会

去侦测比对函数引数的型态签名,不正确的配对会被编译器挑出错误来,而非在执行

时才被挑出。

OO的程式里,最常见的「型态不符」错误是:欲对某物件启动个成员函数,但该物

件并未准备好要处理该运算动作。譬如,如果"Fred"类别有成员函数"f()"但没

有"g()",且"fred"是"Fred"类别的案例,那麽"fred.f()"就是合法的,

"fred.g()"则是非法的。C++(静态地)在编译期捕捉型别错误,Smalltalk则(动

态地)在执行期捕捉。(技术上,C++很像Pascal--“半”静态型别--因为指

标转型与union都能用来破坏型别系统;这提醒了我们:你用指标转型与union的

频率,只能像你用"goto"那样。)

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

Q91:「静态型别」与「动态型别」哪一种比较适合C++?

若你想最有效率使用C++,请把她当成静态型别语言来用。

C++极富弹性,你可以(藉由指标转型、union或#define)让她「长得」像

Smalltalk。但是不要这样做。这提醒了我们:少用#define。

有些场合,指标转型和union是必要的,甚至是很好的做法,但须谨慎为之。指标

转型等於是叫编译器完全信赖你。错误的指标转型可能会毁坏堆积、在别的物件记忆

体中乱搞、呼叫不存在的运作行为、造成一般性错误(generalfailure)。这是很

糟糕的事。如果你避免用与这些相关的东西,你的C++程式会更安全、更快,因为

能在编译期就检测的东西,就不必留到执行期再做。

就算你喜欢动态型别,也请避免在C++里使用,或者请考虑换另一个将型态检查延

迟到执行期才做的语言。C++将型态检验100%都放在编译时期;她没有任何执行期

型态检验的内建机制。如果你把C++当成一个动态型别的OOPL来用,你的命运将

操之汝手。

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

Q92:怎样分辨某个C++物件程式库是否属於动态型别的?

提示#1:当所有东西都衍生自单一的根类别(rootclass),通常叫做"Object"。

提示#2:当容器类别containerclasses,像List、Stack、Set等,都不是

template版的。

提示#3:当容器类别(List、Stack、Set等)把插入/取出的元素,都视为指向

"Object"的指标时。(你可以把Apple放进容器中,但当你取出时,编

译器只知道它是衍生自Object,所以你得用指标转型将它转回Apple*;

你最好祈祷它真的是个Apple,否则你会脑充血的。)

你可用"dynamic_cast"(於1994年才加入的)来使指标转型「安全些」,但这种

动态测试依旧是“动态”的。这种程式风格是C++动态型别的基本要素,你可以呼

叫函数:「把这个Object转换成Apple,或是给我个NULL,如果它不是Apple的

话」,你就得到动态型别了:直到执行时期才知道会发生什麽事。

若你用template去实作出容器类别,C++编译器会静态侦测出99%的型态资讯(

"99%"并不是真的;有些人宣称能做到100%,而那些需要持续性(persistence)的

人,只能得到低於100%的静态型别检验)。重点是:C++透过template来做到泛

型(genericity),而非透过继承。

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

Q93:在C++里怎样用继承?它和Smalltalk有何不同?

有些人认为继承是用来重用程式码的。在C++中,这是不对的。说明白点,「继承

不是『为』重用程式码而设计的。」

【译注】这一个分野相当重要。否则,C++使用者就会感染「继承发烧症」

(inheritancefever)。

C++继承的目的是用来表现介面一致性(产生子类别),而不是重用程式码。C++中

,重用程式码通常是靠「成份」(composition)而非继承。换句话说,继承主要是用

来当作「特异化」(specialization)的技术,而非实作上的技巧。

这是与Smalltalk主要的不同之处,在Smalltalk里只有一种继承的型式(C++有

"private"继承--「共享程式码,但不承袭其介面」,有"public"继承--表现

"kind-of"关系)。Smalltalk语言非常(相对於只是程式的习惯)允许你置放一个

override覆盖(它会去呼叫个「我看不懂」的运作行为),以达到「隐藏住」继承

下来的运作行为的“效果”。更进一步,Smalltalk可让观念界的"is-a"关系“独

立於”子类别阶层之外(子型别不必也是子类别;譬如,你可以让某个东西是一个

Stack,却不必继承自Stack类别)。

相反的,C++对继承的限制更严:没办法不用到继承就做出“观念上的is-a”关系

(有个C++的解决方法:透过ABC来分离介面与实作)。C++编译器利用公共继承

额外附的语意资讯,以提供静态型别。

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

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

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

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

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

出的堆叠,他不必真的从堆叠继承过来;喜欢的话,他可以从阵列类别Array中继

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

物件程式库。取得法:anonymousftp到[128.231.128.7],

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

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

的C++物件程式库。有些NIHCL用到的动态型别很棒(譬如:persistentobjects

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

关系。

详见前面关於Smalltalk的FAQs。

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

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

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

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

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

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

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

运算子来拷贝其值(copy/value语意),或是用指标拷贝方式来拷贝指标

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

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

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

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

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

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

所付出的代价,(通常)会被拥有「真正的物件本身」、而非仅是指向物件的指标所

带来的效益弥补过去。

有三个情况,你会得到真正的物件,而不是指向它的指标:区域变数、整体/静态变

数、完全被某类别包含在内(fullycontained)的成员物件。这里头最重要的就是

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

後面的FAQs会有更多关於copy-vs-reference语意的资讯,请全部读完,以得到

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

所偏颇。

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

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

Q98:「虚拟资料」是什麽?怎麽样/为什麽该在C++里使用它?

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

支援」虚拟资料,但可以模拟出来。不漂亮,但还能用。

欲模拟之,基底类别必须有个指标指向成员物件,衍生类别必须提供一个"new"到

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

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

被参考者。

举例来说,"Stack"类别可能有个Array成员物件(采用指标),衍生类别

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

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

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

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

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

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

优点:

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

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

缺点:

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

*多增加额外的自由记忆体配置负担(new与delete)。

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

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

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

也会。

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

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

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

Q99:虚拟资料和动态资料有何差别?

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

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

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

(staticproperty);它不随任何物件之生命期而动态地改变,同一个子类别的不同

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

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

*「成员函数」-->「成员物件」

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

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

这样子,你就看到虚拟资料的定义。

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

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

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

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

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

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

*虚拟资料:成员物件的定义(真正所属的类别)可被子类别覆盖,只要它的宣告

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

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

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

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

*动态资料:成员物件真正所属的类别,可随时间动态地改变。

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

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

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

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

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

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

成份。

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

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

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

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

标还要好:

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

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

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

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

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

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

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

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

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

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

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

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

integration、暂存器生命期等等事项。三种情形之下,C++编译器能知道物件真

正的类别:区域变数、整体/静态变数、完全被包含的成员物件。

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

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

了。

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

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

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

Yes,可是...

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

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

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

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

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

编译器拥有真正的物件,而非该物件的指标或参考。也就是说:不是区域变数、整体

/静态物件,就是合成物件里的完全包含物件。

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

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

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

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

[DougLea,"CustomizationinC++,"procUsenixC++1990]).

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

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

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

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

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

素。

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

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

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

错。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

不。

前面的FAQ是讨论“成员物件”(memberobject)的,而不是函数参数。一般说来

,位於继承阶层里的物件,应该用参考或指标来传递,而非传值,因为惟有如此你才

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

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

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

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

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
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- 王朝網路 版權所有