Linux 版 (精华区)
发信人: netiscpu (夜☆星光点点☆), 信区: Linux
标 题: ◇ C++ FAQ 1/4
发信站: 紫 丁 香 (Sun Nov 8 17:38:54 1998), 转信
寄信人: guest.bbs@hgluo.hust.edu.cn
标 题: ◇ C++ FAQ 1/4
发信站: 华南理工大学 BBS木棉站
日 期: Thu Feb 20 15:12:15 1997
=======================================
C++语言常见问题解答
(USENET comp.lang.c++ 的 FAQ 文件中译)
原作者:Marshall Cline (cline@cheetah.ece.clarkson.edu)
(cline@parashift.com)
中译者:叶秉哲 (william@tiger.cis.nctu.edu.tw)
原文件:USENET comp.lang.c++ FAQ, Jan 31, 1996.
版权声明:完全依照原作者 Marshall Cline 於此文件中所示之版权事项
(请见下文之 "COPYRIGHT" 部份)。
如欲对本中译文件作授权事项之外的处理,请先洽本译者。
责任事项:完全比照原作者 Marshall Cline 於此文件中所示之声明事项
(请见下文之 "NO WARRANTY" 部份)。
中译事项:笔者虽尽力使此中译文件合乎信、达的要求,但仍恐有未逮之处,
故有任何出入之处,请以原文为准。
任何笔者为中译、诠释所需而加注的地方,都以 【译注】 符号
标示出来。
本译文位置:以 URL (Uniform Resource Locator) 格式说明。
FTP 用户:
ftp://ftp.cis.nctu.edu.tw/Documents/News/C-faq/c-cppfaq.zip
WWW 用户:
ftp://ftp.cis.nctu.edu.tw/Documents/News/C-faq/Index.html
各大 BBS 的 programming 精华区也可能收录,但以前述地点最新。
尔後,随原文件之新版而异动时,会在 news:tw.bbs.comp.language 发布。
<附> 本文件中译的授权:
--- --- --- --- --- Quote Begin --- --- --- --- ---
From cline@cheetah.ece.clarkson.edu Mon Oct 10 11:56:59 1994
Return-Path: <cline@cheetah.ece.clarkson.edu>
Received: from cheetah.ece.clarkson.edu by cis.nctu.edu.tw (4.1/SMI-4.1)
id AA18895; Mon, 10 Oct 94 11:56:46 CST
Received: by cheetah.ece.clarkson.edu (4.1/SMI-4.1)
id AA06374; Sun, 9 Oct 94 23:59:49 EDT
Date: Sun, 9 Oct 94 23:59:49 EDT
From: cline@cheetah.ece.clarkson.edu (Marshall Cline)
Message-Id: <9410100359.AA06374@cheetah.ece.clarkson.edu>
To: is80001@cis.nctu.edu.tw
In-Reply-To: William Yeh's message of Sat, 24 Sep 94 10:31:48 CST
<9409240231.AA20537@cissun51.cis.nctu.edu.tw>
Subject: "C++ FAQ" book from Addison-Wesley
Reply-To: cline@sun.soe.clarkson.edu (Marshall Cline)
Status: OR
>Date: Sat, 24 Sep 94 10:31:48 CST
>From: is80001@cis.nctu.edu.tw (William Yeh)
>Newsgroups: comp.lang.c++
>X-Newsreader: TIN [version 1.2 PL2]
>
>Hi,
>
> We, students at Department of Computer & Information Science, enjoy
>your electronic-formed C++ FAQ very much. We'd like to translate it
>into our native Chinese for those having difficulty to read English
>articles.
>
> The translated article is meant to be used as an electronic document
>on Internet, and not for commercial use. We, therefore, ask for your
>grants for this translation.
>
> Regards.
>
>student at cis.nctu.edu.tw
>--
>-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
> William Yeh | E-mail address:
> | is80001@cis.nctu.edu.tw
> Dept. of Computer & Info. Science | u8023001@cc.nctu.edu.tw
> National Chiao-Tung University, | william.bbs@bbs.cis.nctu.edu.tw
> Hsinchu, Taiwan, R.O.C. |
>-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
That sounds like a wonderful idea.
Permission granted.
M.
--- --- --- --- --- Quote End --- --- --- --- ---
【中译对照表】
底下列出本文出现的专有名词、其他重要的词儿,与译者采用的中译,视上下
文情况,它们也可能会有两种以上的译名。可能的话,括号中也会附上一些其
他书籍文章中常见的译法。
abstraction 抽象化.
access 存取.
access function 存取函数.
alias 别名.
argument 引数.
arity 元数.
assignment 设定.
associativity 结合律.
base class 基底类别.
binding 系结.
call 呼叫.
class 类别、物件类别.
composition 成份、零件.
constructor 建构子(建构元).
container 容器.
container class 容器类别.
data member 资料成员.
declaration 宣告 <名词>.
declare 宣告 <动词>.
default 预设、内定.
define 定义 <动词>.
definition 定义 <名词>.
dereference 解参用、解参考.
derive 衍生.
destructor 解构子(解构元).
dispatch 分派、函数分派.
dynamic 动态.
encapsulation 封装、封装性.
exception 例外、例外状况(异常).
exception handling 例外处理.
explicit 明显的、外显的 <形容词>.
expression 运算式.
friend 夥伴.
function 函数.
genericity 泛型.
header file 标头档(引入档、含括档).
hide 遮蔽.
hierarchy 阶层.
identity 个体识别.
implement 实作 <动词>.
implementation 实作 <名词>.
inherit 继承 <动词>.
inheritance 继承 <名词>.
inline 行内(列内、内嵌).
inspector 查询子.
instance 案例、实体(执行个体).
instantiate 案例化、实体化 <动词>.
instantiation 案例 <名词>.
keyword 关键字、保留字.
mangle 签名编码 <动词>.
mangling 签名编码 <名词>.
member 成员.
member function 成员函数.
member object 成员物件.
method 运作方法、运算方法.
module 模组.
multiple inheritance 多重继承.
mutator 更动子.
object 物件(个体).
OO 物件导向(个体导向).
OOP 物件导向程式设计.
operator 运算子.
overload 多载(过荷、负载) <动词>.
overloading 多载(过荷、负载) <名词>.
override 覆盖、改写.
parameter 参数.
persistence 持续性(永续性、持固性) <名词>.
persistent object 持续性物件.
pointer 指标.
polymorphism 多型(同名异式).
precedence 优先序.
pretty printer 美编工具.
private 私有.
protected 保护.
prototype 原型、函数原型(雏型).
public 公共、公有.
pure virtual function 纯虚拟函数.
reference 参考、参考值.
relation 关系.
return value 传回值.
semantics 语意.
signature 签名、型态签名.
smart pointer 聪明的指标.
specialization 特异化、特殊化.
statement 陈述、指令(指述).
static 静态.
structure 结构、记录.
subclass 子类别、子代类别.
subtype 子型别、子型态.
superclass 父类别、亲代类别.
syntax 语法、文法.
template 样版(模版).
throw 丢出.
type 型态、型别.
virtual 虚拟.
virtual function 虚拟函数.
【中译版本异动纪录】
85.02. 对整篇译文做比较多一点的修饰。
84.05. 修饰些 method 相关的译名。
84.03. 修饰些 persistence、instance 相关的译名。
84.01. 一些译文修饰、增加此中译文件的 ftp 栖身处说明。
将覆盖 (override) 与遮蔽 (hide) 二词更明显地区分开来。
对「聪明的指标」(smart pointer) 加一点诠释。
83.12. 一些译文修饰、将 "method" 统一译为「运算方法」。
83.11. 第一次发布,原载於交大资科系刊「资讯人园地」。
底下就是此 USENET comp.lang.C++ FAQ 文件的正文。
== Part 1/4 ============================
【译注】原文件因篇幅过长,在发布时就已分成四份。
comp.lang.c++ Frequently Asked Questions list (with answers, fortunately).
Copyright (C) 1991-96 Marshall P. Cline, Ph.D.
Posting 1 of 4.
1/96 更新:
* 关於恢复 fron.soe.clarkson.edu 的 anonymous ftp 方式仍无下文。
* 本月份没什麽变动。
9/95 更新:
* 加入 FAQ 41:以变数做为多维阵列的第一维度。
* 加入 FAQ 123:关於 "Numerical Recipes" 的程式。
* 加入第 20 节 ("程式库")。内容还很少,但总是个开端。
* 修正 FAQ 30 的错误(少了 "i" 变数)。
* 加入 FAQ 124:使用动态连结以避免过於庞大的执行档。
* 加入 FAQ 32:想在 binary 模式下 "reopen" cin 及 cout 的问题。
* 加入 ftp ANSI/ISO Committee Draft 的资讯 (FAQ 7)。
7/95 更新:
* 小地方(除了下面会提到的 FTP 地点的变动之外)。
6/95 更新:
* 更正订阅 ANSI-C++ Draft 的电子邮递信箱。
* 添加关於浮点运算的 FAQ [Phil Staite 所提的]。
* 添加关於多维阵列的 FAQ [Doug Shapter 所提的]。
* 添加关於中断服务常式 (ISR),以及指向成员函数的指标之 FAQ。
* 重排关於「用 'new' 来配置某些类别的物件」FAQ 的位置。
5/95 更新:
* 一些语句修饰。
4/95 更新:
* 添加 BC++ Windows 方面常见的问题。
* 更改 NIHCL 的 ftp 位址。
* 添加解释:"ARM" 代表 "Annotated Reference Manual"。
3/95 更新:
* 添加关於 "delete this" 的问题。
* 添加两则关於 iostreams 与 eof 的问题。
* 更正些 Gnu emacs 的 "c-mode" 和 "cc-mode" 的项目。
1/95 更新:
* 此文件的中译版出来了;底下再详述之。
12/94 更新:
* 添加关於 STL 的 FAQ(放在 #115)。
* 添加关於签名编码的 FAQ(放在 #119)。
* 更正「『成份』与『私有继承』之比较」这项 FAQ 里面的一些打字错误。
* 更正一些拼字错误。
11/94 更新:
* 加入「FAQ 书」和「FAQ 文件」的区别资讯。
* 其他字面上的修饰。
10/94 更新:
* 修正一些打字错误。
9/94 更新:
* 一点文字上的修饰。
8/94 更新:
* 加入 "typeid" 和 "dynamic_cast" 的新规定。
* 加入 "mutable" 和 "const_cast" 的新规定。
* 重写大部份的回答部份,使其更一致些。
* 原文的引号改为 "..." 而不是 `...' 或 ``...'' 。
* 有程式码例子的那一行以 TAB 起头;其他行则否。
【译注】本中译版以 8 个英文空白字元代替 TAB。
* 所有东西都编辑过了;到处都有小更动。
=======================
■□ 第1节:内容介绍
=======================
DOCUMENT: Frequently-Asked-Questions for comp.lang.c++
REVISION: Jan 31, 1996
文件不等於书本:这份 C++ FAQ 文件和「FAQ 书」是不一样的。“FAQ 书”
("C++ FAQs", Addison-Wesley, 1995) 是这篇文件的五倍大。
底下有更详细的介绍。
AUTHOR: Marshall P. Cline, Ph.D.
Paradigm Shift, Inc.
One Park St. / Norwood, NY 13668
voice: 315-353-6100
fax: 315-353-6110
email: cline@parashift.com
COPYRIGHT: Copyright (C), 1991-96 Marshall P. Cline, Ph.D.
Permission to copy all or part of this work is granted,
provided that the copies are not made or distributed
for resale (except a nominal copy fee may be charged),
and provided that the AUTHOR, COPYRIGHT, & NO WARRANTY
sections are retained verbatim and are displayed
conspicuously. If anyone needs other permissions that
aren't covered by the above, please contact the author.
NO WARRANTY: THIS WORK IS PROVIDED ON AN "AS IS" BASIS. THE AUTHOR
PROVIDES NO WARRANTY WHATSOEVER, EITHER EXPRESS OR
IMPLIED, REGARDING THE WORK, INCLUDING WARRANTIES WITH
RESPECT TO ITS MERCHANTABILITY OR FITNESS FOR ANY
PARTICULAR PURPOSE.
版权声明:Copyright (C), 1991-96 Marshall P. Cline, Ph.D.
复制本文件全部或部份的内容,若合乎下述诸项要求,则允许之
:不得为转售之目的而制作或传播任何复制品(但可索取名义上
的传播手续费),并且作者栏、版权声明及责任事项的部份,要
一字不漏地、醒目地显示出来。若您需要此处未提及的授权事项
,请洽本作者。
责任事项:这份文件是供您参考用的。对於本作品可能造成的营利性、或任
何特殊场合适用性之保障,作者概不负责,不论是否曾明白地指
出或暗示。
【译注】上述的「版权声明」与「责任事项」译文,仅供参考,一切请以
原文为准。译者对它们亦概不负责…… ;->
文件取得方法: 旧的 FTP 地点不能用了。我正在寻找新的地点,
请密切注意下个月的这个地方。
请不要寄信来推荐新的地点(我的信箱会爆满的 :)
【译注】台湾使用者,请到以下地点,或其 AFS client 取得:
ftp://NCTUCCCA.edu.tw/USENET/FAQ/comp/lang/c++/FAQ*
其他资讯:comp.lang.c FAQ 每个月都会出现在该讨论区中,
该文件的维护者是 Steve Summit (scs@eskimo.com) 。
中文翻译:用 "Big5" 内码(在台湾最广为使用的 16 位元中文内码)储存
的中文译文,可用 anonymous ftp 到以下地点取得:
ftp://ftp.cis.nctu.edu.tw/Documents/News/C-faq/c-cppfaq.zip
=================================
● 1A:「FAQ 书」与「FAQ 文件」
=================================
Addison-Wesley 已出版了由 Cline 与 Lomow 合著的 "C++ FAQs" (1995, ISBN
0-201-58958-3) 一书,许多人对该书和这篇文件的关系感到好奇,一些没看过该书
的人,也担心本文是否会和它相同。此处将回答这些问题。
「FAQ 书」(於 Oct. 94 上市)大体上和本文的观念一致,但该书约为本文的5倍
大,还包括了数千则交互参考资讯、参考资料、索引项目,及许多的程式例子。
=============
● 1B:目录
=============
========== POSTING #1 ==========
第1节:内容介绍
----------------
⊙1A:「FAQ 书」与「FAQ 文件」
⊙1B:目录
⊙1C:术语及常用的缩写
第2节:我该如何参与讨论?(发信之前请务必一读)
------------------------------------------------
Q1:我该在哪个讨论区中发问?
Q2:我该怎麽提出「我的程式有毛病」的问题呢?
第3节:周遭的、管理上的事项
----------------------------
Q3:什麽是 OOP?什麽是 C++?
Q4:C++ 的优点是什麽?
Q5:谁在用 C++?
Q6:有任何 C++ 标准化方案在进行吗?
Q7:该到哪里索取最新的 ANSI-C++ 标准草案?
Q8:C++ 对 ANSI-C 回溯相容吗?
Q9:多久才能学会 C++?
第4节:C++ 的基础
------------------
Q10:什麽是类别(class)?
Q11:什麽是物件(object)?
Q12:什麽是参考(reference)?
Q13:如果设定某值给参考会怎麽样?
Q14:怎样才能将参考改设成别的物件?
Q15:何时该用参考,何时又该用指标?
Q16:行内函数是做什麽的?
第5节:建构子和解构子
----------------------
Q17:建构子(constructor)是做什麽的?
Q18:怎样才能让建构子呼叫另一个同处一室的建构子?
Q19:解构子(destructor)是做什麽的?
第6节:运算子多载
------------------
Q20:运算子多载(operator overloading)是做什麽的?
Q21:哪些运算子可以/不能被多载?
Q22:怎样做一个 "**"「次方」运算子?
第7节:夥伴
------------
Q23:夥伴(friend)是什麽?
Q24:「夥伴」违反了封装性吗?
Q25:夥伴函数的优缺点?
Q26:「夥伴关系无继承及递移性」是什麽意思?
Q27:应该替类别宣告个成员函数,还是夥伴函数?
第8节:输入/输出:<iostream.h> 和 <stdio.h>
---------------------------------------------
Q28:该怎样替 "class Fred" 提供输出功能?
Q29:为什麽我该用 <iostream.h> 而不是以前的 <stdio.h>?
Q30:为什麽我处理输入时,会超过档案的结尾?
Q31:为什麽我的程式执行完第一次回圈後,会对输入的要求不加理睬?
Q32:在 DOS 及 OS/2 的 binary 模式下,要怎样来 "reopen" cin 及 cout?
========== POSTING #2 ==========
第9节:自由记忆体管理
----------------------
Q33:"delete p" 会删去 "p" 指标,还是它指到的资料,"*p" ?
Q34:我能 "free()" 掉由 "new" 配置到的、"delete" 掉由 "malloc()" 配置到的
记忆体吗?
Q35:为什麽该用 "new" 而不是老字号的 malloc() ?
Q36:为什麽 C++ 不替 "new" 及 "delete" 搭配个 "realloc()" ?
Q37:我该怎样配置/释放阵列?
Q38:万一我忘了将 "[]" 用在 "delete" 由 "new Fred[n]" 配置到的阵列,会发生
什麽事?
Q39:成员函数做 "delete this" 的动作是合法的(并且是好的)吗?
Q40:我该怎麽用 new 来配置多维阵列?
Q41:C++ 能不能做到在执行时期才指定阵列的长度?
Q42:怎样确保某类别的物件都是用 "new" 建立的,而非区域或整体/静态变数?
第10节:除错与错误处理
------------------------
Q43:怎样处理建构子的错误?
Q44:如果建构子会丢出例外的话,该怎麽处理它的资源?
第11节:Const 正确性
----------------------
Q45:什麽是 "const correctness"?
Q46:我该早一点还是晚一点让东西有常数正确性?
Q47:什麽是「const 成员函数」?
Q48:若我想在 "const" 成员函数内更新一个「看不见的」资料成员,该怎麽做?
Q49:"const_cast" 会不会丧失最佳化的可能?
第12节:继承
--------------
Q50:「继承」对 C++ 来说很重要吗?
Q51:何时该用继承?
Q52:怎样在 C++ 中表现出继承?
Q53:把衍生类别的指标转型成指向它的基底,可以吗?
Q54:Derived* --> Base* 是正常的;那为什麽 Derived** --> Base** 则否?
Q55:衍生类别的阵列「不是」基底的阵列,是否表示阵列不好?
⊙12A:继承--虚拟函数
Q56:什麽是「虚拟成员函数」?
Q57:C++ 怎样同时做到动态系结和静态型别?
Q58:衍生类别能否将基底类别的非虚拟函数覆盖(override)过去?
Q59:"Warning: Derived::f(int) hides Base::f(float)" 是什麽意思?
⊙12B:继承--一致性
Q60:我该遮蔽住由基底类别继承来的公共成员函数吗?
Q61:圆形 "Circle" 是一种椭圆 "Ellipse" 吗?
Q62:对「圆形是/不是一种椭圆」这两难问题,有没有其他说法?
⊙12C:继承--存取规则
Q63:为什麽衍生的类别无法存取基底的 "private" 东西?
Q64:"public:"、"private:"、"protected:" 的差别是?
Q65:当我改变了内部的东西,怎样避免子类别被破坏?
⊙12D:继承--建构子与解构子
Q66:若基底类别的建构子呼叫一个虚拟函数,为什麽衍生类别覆盖掉的那个虚拟函
数却不会被呼叫到?
Q67:衍生类别的解构子应该外显地呼叫基底的解构子吗?
⊙12E:继承--Private 与 protected 继承
Q68:该怎麽表达出「私有继承」(private inheritance)?
Q69:「私有继承」和「成份」(composition) 有多类似?
Q70:我比较该用哪一种:成份还是私有继承?
Q71:我应该用指标转型方法,把「私有」衍生类别转成它的基底吗?
Q72:保护继承 (protected inheritance) 和私有继承有何关连?
Q73:"private" 和 "protected" 的存取规则是什麽?
第13节:抽象化(abstraction)
-------------------------------
Q74:分离介面与实作是做什麽用的?
Q75:在 C++ 里,我该怎样分离介面与实作(像 Modula-2 那样)?
Q76:ABC ("abstract base class") 是什麽?
Q77:「纯虚拟」(pure virtual) 成员函数是什麽?
Q78:怎样替整个类别阶层提供列印的功能?
Q79:何时该把解构子弄成 virtual?
Q80:虚拟建构子 (virtual constructor) 是什麽?
========== POSTING #3 ==========
第14节:程式风格指导
----------------------
Q81:有任何好的 C++ 程式写作的标准吗?
Q82:程式撰写标准是必要的吗?有它就够了吗?
Q83:我们的组织该以以往 C 的经验来决定程式撰写标准吗?
Q84:我该在函数中间或是开头来宣告区域变数?
Q85:哪一种原始档命名惯例最好? "foo.C"? "foo.cc"? "foo.cpp"?
Q86:哪一种标头档命名惯例最好? "foo.H"? "foo.hh"? "foo.hpp"?
Q87:C++ 有没有像 lint 那样的指导原则?
第15节:Smalltalk 程式者学习 C++ 之钥
---------------------------------------
Q88:为什麽 C++ 的 FAQ 有一节讨论 Smalltalk?这是用来攻击 Smalltalk 的吗?
Q89:C++ 和 Smalltalk 的差别在哪?
Q90:什麽是「静态型别」?它和 Smalltalk 有多相似/不像?
Q91:「静态型别」与「动态型别」哪一种比较适合 C++?
Q92:怎样分辨某个 C++ 物件程式库是否属於动态型别的?
Q93:在 C++ 里怎样用继承?它和 Smalltalk 有何不同?
Q94:Smalltalk/C++ 不同的继承,在现实里导致的结果是什麽?
Q95:学过「纯种」的 OOPL 之後才能学 C++ 吗?
Q96:什麽是 NIHCL?到哪里拿到它?
第16节:参考与数值语意
------------------------
Q97:什麽是数值以及参考语意?哪一种在 C++ 里最好?
Q98:「虚拟资料」是什麽?怎麽样/为什麽该在 C++ 里使用它?
Q99:虚拟资料和动态资料有何差别?
Q100:我该正常地用指标来配置资料成员,还是该用「成份」(composition)?
Q101:动态配置成员物件有三个效率因素,它们的相对代价是多少?
Q102:"inline virtual" 的成员函数真的会被 "inline" 吗?
Q103:看起来我不应该用参考语意了,是吗?
Q104:参考语意效率不高,那麽我是否应该用传值呼叫?
========== POSTING #4 ==========
第17节:和 C 连结/和 C 的关系
--------------------------------
Q105:怎样从 C++ 中呼叫 C 的函数 "f(int,char,float)"?
Q106:怎样才能建一个 C++ 函数 "f(int,char,float)",又能被 C 呼叫?
Q107:为什麽 linker 有这种错误讯息:C/C++ 函数被 C/C++ 函数呼叫到?
Q108:该怎麽把 C++ 类别的物件传给/传自 C 的函数?
Q109:C 的函数能不能存取 C++ 类别的物件资料?
Q110:为什麽我总觉得 C++ 让我「离机器更远了」,不像 C 那样?
第18节:指向成员函数的指标
----------------------------
Q111:「指向成员函数的指标」和「指到函数的指标」的型态有差别吗?
Q112:怎样把指向成员函数的指标传给 signal handler、X event callback 等等?
Q113:当我想以成员函数做为中断服务常式 (ISR) 时,为什麽编译器产生(型态不
符)的错误?
Q114:为什麽我取不出 C++ 函数的位址?
Q115:怎样宣告指向成员函数的指标阵列?
第19节:容器类别与 template
-----------------------------
Q116:怎样自一个连结串列/杂凑表等等里面,插入/存取/改变元素?
Q117:「样版」(template)的用意是什麽?
Q118:"function template" 的语法/语意是什麽?
Q119:"class template" 的语法/语意是什麽?
Q120:什麽是「参数化型别」(parameterized type)?
Q121:「泛型」(genericity)是什麽?
第20节:程式库
----------------
Q122:怎样拿到 "STL"?
Q123:怎样 ftp 到 "Numerical Recipes" 附的程式?
Q124:为什麽我的执行档会这麽大?
第21节:特定系统的细节
------------------------
Q125:GNU C++ (g++) 把小程式造出大大的执行档,为什麽?
Q126:有 YACC 的 C++ 文法吗?
Q127:什麽是 C++ 1.2? 2.0? 2.1? 3.0?
Q128:如果签名编码标准化了,我能否将不同厂商编译器产生的程式码连结起来?
第22节:其他的技术和环境的事项
--------------------------------
⊙22A:其他的技术事项
Q129:为什麽有 static 资料成员的物件类别产生了 linker 错误?
Q130:"struct" 和 "class" 关键字差别在哪?
Q131:为什麽不能以函数的传回值来多载(overload)它?
Q132:什麽是「持续性」?什麽是「持续性物件」?
Q133:为什麽浮点数 (floating point) 这麽不精确?为什麽这段程式不会印出 0.43?
⊙22B:其他环境下的琐事
Q134:有任何 TeX 或 LaTeX 的巨集,能处理 "C++" 的留白效果(spacing)吗?
Q135:在哪儿可拿到 C++2LaTeX 这个 C++原始码的 LaTeX 美编工具(pretty
printer)?
Q136:该到哪里取得 "tgrind" 这个 C++/C/etc 的原始码美编工具?
Q137:有给 GNU emacs 编辑器用的 C++-mode 吗?有的话,该怎麽拿?
Q138:我要到哪儿得到和作业系统相关的 FAQs( 譬如:BC++、DOS、Windows 等等)?
Q139:为什麽我的 DOS C++ 程式说 "Sorry: floating point code not linked"
“抱歉,浮点运算程式码未连结进来”?
Q140:为什麽当我没执行 BC45 IDE 的话,BC++ 做出来的 Windows 应用程式就不能用?
=========================
● 1C:术语及常用的缩写
=========================
这儿是一些此文件所采用的缩写:
字汇 意义
==== ===========
fn function ,函数(单数型)
fns functions,函数(复数型)
param parameter,参数
ptr pointer,指标,C/C++ 的语法元素,宣告法: int * p;
ref reference,参考,C++ 的语法元素,宣告法: int & r;
OO object-oriented,物件导向
OOP object-oriented programming,物件导向程式设计
OOPL object-oriented programming language,物件导向语言
method 运作行为,"member function 成员函数" 的另一种说法
【译注】"method" 是源自 Smalltalk 的术语,很常用於 OO 界。
=======================================================
■□ 第2节:我该如何参与讨论?(发信之前请务必一读)
=======================================================
Q1:我该在哪个讨论区中发问?
Comp.lang.c++ 是讨论 C++语言本身最好的地方(譬如:C++ 程式设计、语法、风格
)。其他讨论区是用来讨论特定的系统(譬如:MS Windows 或是 UNIX),或是其他
和 C++语言不直接相关的主题(譬如:怎样使用你的编译器)。底下列出一些非常热
门的讨论区,并从它们的 FAQs 中摘录些片断,应该能让您明了它们最常讨论哪些课
题。
comp.os.ms-windows.programmer.tools
此区是用来讨论有关 Windows 软体发展系统工具的选择及使用。
comp.os.ms-windows.programmer.misc
此乃论及其馀 Windows 软体发展之事项。
[有个 FAQ 列表,列出所有 comp.os.ms-windows.programmer.* 讨论区]
FAQ 5.7.1. 在 DLL 中存取 C++ 的物件类别
FAQ 6.1.1. 以 MDI 子视窗做出对话框 [用 OWL]
FAQ 6.2.1. 把禁能的选项致能起来 [用 MFC]
FAQ 8.1.5. 使用 windows.h 的 STRICT 符号定义
FAQ 10. 程式设计参考资料
comp.os.msdos.programmer
许多信件都是关於程式语言产品的(主要是 Borland 和 Microsoft)。
FAQ 301. 怎样才能读取字元而不 [等待] Enter 键?
FAQ 412. 怎样读取、建立、更改及删除磁片标名?
FAQ 504. 怎样设定 COM 埠,以用它来传输资料?
FAQ 602. C 程式怎样才能送控制码给印表机?
FAQ 606. 怎样才能得知 Microsoft 滑鼠的位置及按钮状态?
FAQ 707. 怎样写常驻程式(TSR)工具?
FAQ B0. 怎样连系 [Borland, Microsoft] 等公司?
[注意:这份 FAQ 不在 rtfm.mit.edu 里;而在 Simtel
(譬如 oak.oakland.edu) in /pub/msdos/info/faqp*.zip 以及 Garbo
(garbo.uwasa.fi) in /pc/doc-net/faqp*.zip]
comp.os.msdos.programmer.turbovision [Borland 的文字模式应用程式骨架]
comp.unix.programmer
FAQ 4.5) 怎样使用 popen() 开启行程以读写之?
FAQ 4.6) 怎样在 C 程式里 sleep() 一秒以内?
comp.unix.solaris (包含 SunOS 4.x 和 Solaris)
FAQ 4) Signal 入门
FAQ 5) 等待子行程 Exit
gnu.g++.help
FAQ: 到哪里找 C++ 的 demangler(反签名编码器)?
FAQ: 哪里有 Solaris 2.x 版的 gcc/g++ 位元档?
FAQ: 有 g++ 2.x 的文件吗?
gnu.g++.bug [g++ 的臭□列表 -- 请见 g++ 的文件]
comp.lang.c
FAQ 1.10: 我搞糊涂了。NULL 保证一定是 0,但是 null 指标却不是?
FAQ 2.3: 那麽,在 C 里头「指标和阵列等价」是什麽意思?
FAQ 4.2: [为什麽 "printf("%d\n," i++ * i++);" 有问题?]
FAQ 7.1: 怎样写一个接收不定数目引数的函数? [stdarg.h 或是 varargs.h]
FAQ 10.4: 怎麽宣告一个指向某种函数的指标阵列,而该函数的传回值为:
指向另一个传回字元指标的函数?
并请参考看看 comp.graphics、comp.sources.wanted、comp.programming,以及
comp.object(它的 FAQ 是个很棒的 OOP 入门、术语观念概论文件)。请记住:
comp.std.c++ 是专门讨论和研议中的 ANSI/ISO C++ 标准方案(下文会提)“直接
”相关的事项。
同时到上述信区和 comp.lang.c++ 去问同一个问题,几乎是没必要的(你是知道的
,特定系统信区的读者不用机器语言写程式)。只因你的问题「真的很要紧」,就到
处发问,是个很坏的习惯。如果你在「正确的」信区没得到回音,且认为你非得在这
儿发信不可,请至少考虑一下,将这儿的回信重导回原来那个适当的信区。
在任何信区发问之前,你应当先读读它的 FAQ。你想问的可能就在上面,这样就可省
下你发信的时间,以及全世界数以千计的人类读你的信的时间。回答已经是 FAQ问题
的人,可能会因为白白浪费时间而烦扰不已;他们也可能会给你错误或不完整的解答
,因为他们也没看过 FAQ。
「常见问题解答」文件每天 24 小时都可由 anonymous ftp (rtfm.mit.edu 的
/pub/usenet/comp.what.ever) 或是 e-mail server (寄一则内容为 "help" 的信到
mail-server@rtfm.mit.edu) 来取得。欲知详情,请见 "Introduction to the
*.answers newsgroups" 这份文件,它在 news.answers 或 news.announce.newusers
(这儿还有许多必须一读的文件)中找到。
========================================
Q2:我该怎麽提出「我的程式有毛病」的问题呢?
底下是一些建议,让 comp.lang.c++ 的读者能帮你解决程式设计的问题。
1. 请读读上一个问题,以确定你的问题是针对 C++语言本身,而和你的程式设计系
统(譬如:绘图、印表机、设备……)或是编译环境(譬如:「整合环境挂了」
、「怎样消除xxxx警告讯息」、「怎样连结程式库」)完全无关。如果你想知道
为什麽你 OWL程式中的虚拟函数 CmOk() 没被呼叫到,你的问题可能比较适合放
在 Windows程式设计的信区。如果你能写个独立的小程式,而它会让编译器产生
和你那个 OWL程式同样的错误讯息或行为的话,就可以放到 comp.lang.c++ 了,
其他系统的 C++程式员可能帮得上忙。
2. 「信件标题」栏位要有意义。像是「C++ 程式」这样的标题太空泛了,「new 一
个多维阵列的问题」就很好。不要用一堆惊叹号,穷嚷嚷著「救命啊」,或是开
玩笑的用「SEX SEX SEX」这种标题。如果你认为该问题和你的编译器有关,最好
在标题栏中道出编译器和版本编号。
3. 列出完整的、可编译得过去的程式码。要从人类的语言叙述里,去除错或是重建
回一个程式,是极为困难的事。「完整的程式码」指的是:任何被用到的型别、
函数都要宣告出来,被用到的标头档都要 #include 进来……等等。请将程式码
裁减到只留必要的部份,我们并不需要那些执行起来(甚至连结时)“有用的”
东西,我们只须能重现出你的错误讯息(可能在不同的编译器中)就行了。「可
编译得过去」指的是:不要含有一堆未注解掉的 ... 这种删节号,或是各行行首
的行号:
14: #include <iostream.h>
15: class Foo { ... }; // 像这样就是很讨人厌的东西!
将你的程式组织成线性结构,不要让我们再切割、制造些标头档案。请仔细输入
你的程式码--我们通常不容易判断:某个地方只是你的打字错误,抑或它真的
就是你的问题所在。尽量改用编辑器的「剪贴」或「插入档案」功能。
4. 列出你用的编译器、编译器版本,以及你使用的系统。我知道我刚刚说过:特定
系统的问题要去特定的信区发问,但和编译器有关的资讯,常常对侦查问题有帮
助(「喔,我记得 Acme 1.2 在这方面有很多毛病」),这也顺便提醒了那些编
译器的用户:小心那些毛病。
5. 把编译器、连结器的选项写出来,以及你用来建程式所用的程式库。
6. 把错误讯息和何处发生错误的资料写出来。像是「虚拟函数不能用了」并没告诉
我们这是个编译时段、连结时段还是执行期的问题。如果这问题是执行期发生的
,请把它的行为,和任何相关的系统设定资讯列出来。
7. 在签名档中列出真的能用的 e-mail 地址。如果你信件的 "From:" 一栏有错的话
,请通知你的系统管理者。在它修复前,於你的信件标头中加入 "Reply-To:" 一
栏,填上你正确的 e-mail 地址。
8. 请读读这份 FAQ 的其他部份--可能你的问题,或是很相关的问题就在这儿。
谢谢您,并希望以上的建议能协助您找到问题的解答。
===================================
■□ 第3节:周遭的、管理上的事项
===================================
Q3:什麽是 OOP?什麽是 C++?
物件导向(OO)程式技术,是我们所知发展大型而复杂的软体系统最好的方法。
C++ 是个物件导向的程式语言。C++ 可当成一个物件导向程式语言(OOPL),亦可只
当成一个“更好的 C 语言”来使用。不过,若你只把它当成“更好的 C”,你就无
法获得物件导向程式设计的好处。
提一则 OO 的广告词:软体工业刻正无法应付大型而复杂的软体系统需求。但这正是
肇因於我们的「成果」:我们过去的成功促使大家要求得更多,不幸的是,这份市场
的渴求却是「结构化」分析(analysis)、设计(design)和程式设计所无法满足的
。因此,我们才得发展一个更好的典□(paradigm)。
========================================
Q4:C++ 的优点是什麽?
「C++ 的成长」:C++ 是到目前为止最受欢迎的语言。每 7.5到 9个月 C++的使用者
都会加倍。「懂 C++」是个很好的求职资格(但你必须把它当成 OOPL,而不只是一
个更好的 C 来用才行)。
「封装性 encapsulation」:藉由隐藏内部的资料结构,让我们可以改变系统的某部
份,而不必更动其他部份。我们为软体元件(称之为 class,类别)提供一个安全的
介面,用户只碰得到这个介面而已;而相对起来比较容易变动的介面「实作」部份,
就被封装起来(就像被包在胶囊里),以避免用户过於依赖他一时的实作决定。在比
较简单的 C 里头,可由模组内的静态(static)资料来办到,以避免其他模组存取
到它。
「多重案例 multiple instances」:典型的 C 语言「封装」方法(刚才有提),做
不到多重的资料案例(我们很难替模组的 "static" 资料做出多重案例)。如果在 C
中要做到的话,我们得使用 "struct" 结构(但是它没有「封装性」)。在 C++里,
我们可用 "class"(物件类别)来做到多重案例与封装性:"public"公共部份包含了
它的介面(通常这里会有个特别的函数:成员函数),"private" 私有部份包含了它
的实作细节(通常这儿就是内部资料结构的所在)。
「行内函数呼叫」:在 C 中,可以在 struct 里放个 "void*"(该存取函数 [access
functions] 会用到指标转型)来达到「封装的 structs」。这样会丧失型别安全性
,而且会造成过多的函数呼叫,即使你只存取结构内的小小栏位(假如你允许直接存
取结构内栏位的话,它内部的资料结构就很难再变更了,因为你的程式有太多地方“
依赖”它以前的样子)。函数呼叫的额外负担不大,但是会累积起来。C++ 的类别允
许函数作 "inline" 行内扩展,就有以下好处:□封装的安全性,□多重案例的方便
性,□直接存取的速度。而且,编译器也会检查行内函数的参数,这就比 C 的
#define 巨集更好了。
「多载运算子」:C++ 能对物件类别的运算子加以多载(overload),以合乎我们的
直觉(譬如,"myString + yourString" 可做字串串接,"myDate++"可用来递增日期
,"z1 * z2" 可将两复数 z1 及 z2 相乘,"a[i]" 可用来存取 "a" 这个连结串列的
第 i 个元素……等等)。你甚至可以做出个“聪明的指标”(smart pointer),以指
向磁碟或其他地方去("x = *p" 可 dereference [解参用] 指标,也就可以在磁碟
中找到 p 所“指到”的地方,并传回其值)。这可让使用者以切近该问题的方式来
写程式,而非以机器的语言来解题。
【译注】STL (Standard Template Library) 就大量利用到「聪明的指标」功能。
「继承性 inheritance」:我们还只是在表层而已,事实上,我们还没进入「物件导
向」的部份呢!假设你有个 Stack 堆叠型态,有 push、pop 运算。若你还想要个
InvertableStack 型态,它“很像”Stack,只是它还有个 "invert" 运算。以 C 的
方式,你不是得□修改现存的 Stack模组(如果它在其他地方也用到的话,就麻烦了
),就是得□把 Stack拷贝到另一个档案,再加以修改之(这会导致过多重复的程式
码、容易破坏到 InvertableStack 里某些源自 Stack 的小地方,尤有甚者,得维护
双倍的程式码)。C++提供了更乾净的解决法:继承。你可以说:「InvertableStack
继承了 Stack的一切,且 InvertableStack又添加了 invert 运算。」这样子就好了
!Stack本身仍然是封闭的(未被更动到),而 InvertableStack也没重复 push/pop
等的程式码。
「多型」与「动态系结」:OOP 真正的力量不仅是继承性,还有把 InvertableStack
当成是一个 Stack来传递的能力。这是安全的,因为(至少在 C++里)此乃「是一个
……」的关系("is-a" relation),透过公共继承达到的(亦即:InvertableStack
“是一个”Stack,且它还能自我 invert 反转)。多型(polymorphism)与动态系
结(dynamic binding)最容易从实例来理解了,所以我提个典型的例子:绘图软体
得处理圆形、方形、矩形、多边形及直线,这些都是「形状 shape」。大部份绘图软
体的内部函数都需要个“形状”的参数(相对於某些像是“方形”这种特定的形状)
,譬如:当我们用滑鼠选取某个图形,它就可能被拖曳放到萤幕某处。多型和动态系
结让程式能正确运作,即使编译器只知道该参数是个「形状」,而不知它到底是什麽
形状。我们再假设刚才提到的 "pick_and_drag(Shape*)" 函数於星期二编译好了,
到了星期三,你打算再加个六边形。听起来很奇怪,但 pick_and_drag() 仍然能够
处理这个六边形,即使当 pick_and_drag() 编译时六边形还不存在!(若你明了
C++ 是怎麽做的,它就再也不惊异了--但它仍然是很方便的!)
========================================
Q5:谁在用 C++?
很多很多的公司及政府部门。相当的多。
统计上来看:当你正在读这份 FAQ文字时,就有 5 个人正成为 C++的程式员。
========================================
Q6:有任何 C++ 标准化方案在进行吗?
有的;ANSI(美国的)和 ISO(国际的)组织正密切合作。ANSI-C++ 委员会称为
"X3J16" ,而 ISO C++ 标准团体称为 "WG21"。ANSI/ISO C++ 的标准过程中包含了
这些人:
AT&T, IBM, DEC, HP, Sun, MS, Borland, Zortech, Apple, OSF 等等等等。每次开
会约有 70 人,他们来自美、英、日、德、瑞典、丹麦、法国……(他们都有「区域
性」的委员会,派遣正式代表并主导「区域性」的会议)。
========================================
Q7:该到哪里索取最新的 ANSI-C++ 标准草案?
ISO Committee Draft for C++ 以及 ANSI C++ Draft(将要供 public review 的文
件)可如此取得:
http://www.cygnus.com/~mrs/wp-draft
你也可以拿到 Postscript 和 Adobe Acrobat 的版本:
ftp://research.att.com/dist/stdc++/WP
也能拿到 HTML 和 ASCII 的版本:
ftp://ftp.cygnus.com/pub/g++
也能拿到书面版本:
X3 Secretariat
1250 Eye Street NW
Suite 200
Washington, DC 20005
202-626-5738
你也可以用 email:
lbarra@itic.nw.dc.us (Lynn Barra)
注明要索取最新的 "Draft Proposed American National Standard for Information
Systems -- Programming Language C++",文件编号 CD14882。它通常是用2日期的
FedEx(美国境内)来递送的,所以很快就能收到。
========================================
Q8:C++ 对 ANSI-C 回溯相容吗?
几乎是。
C++ 尽可能地和 C 相容,但不能更相容了。事实上,主要的不同在於 C++ 要求函数
原型:"f()" 宣告的是无参数的函数(在 C 里,"f()" 和 "f(...)" 是一样的)。
还有些细微的差别,像在 C++ 里 sizeof('x') 等同於 sizeof(char),但在 C 里面
却是等同於 sizeof(int)。 而且,C++ 直接就把结构的标签(tag)当成是型别的名
字,但 C 就需要加个 "struct" 字("typedef struct Fred Fred" 这种技巧仍然能
用,但在 C++ 中是累赘的)。
========================================
Q9:多久才能学会 C++?
像 Paradigm Shift 公司,成功地教授过标准的工业界「短期课程」,将大学一学期
的课压缩到一周 40 小时。然而真正的精通得由实际经验而来:没有东西能取代时间
。需动手做的指定专题是必要的,因为它们能将你的观念「凝固成形」。
大约要 6-12 个月才能流利使用 C++/OOP,如果身边有高手的话,费时会短些;反之
若没有个“好的”通用型 C++物件程式库,则会耗时更久。想成为顾问级的高手,则
约需 3 年。
有些人却根本办不到。除非你是可造之材,且有强烈的个人驱动力,否则你也做不到
。「孺子可教」最起码的要求是:你必须能「觉今是而昨非」。「驱动力」最起码的
要求是:你愿意多投入时间精力(改变思考的方式〔典□转移 paradigm shift〕要
远比学些新的东西来得困难)。
=========================
■□ 第4节:C++ 的基础
=========================
Q10:什麽是类别(class)?
物件导向系统的基石。
类别是用来定义资料型态(data type)的,就像 C 的 struct 一样。
以资讯科学术语来说,一个型态包含了一组状态(state),以及在状态之间转移的
动作行为(operation)。因此 "int" 是个「型态」,因为它有一组状态,还有诸如
「加两个整数」、「整数相乘」等等的运作行为。同样的,「类别」提供一组(通常
是公共的)运算,及一组(通常是非公共的)资料栏位,以代表该型态的案例所拥有
的抽象值。以 C 的角度来看,类别就是其成员(members)皆预设为 "private" 的
struct。
把 "int" 想成是个类别,它拥有 "operator++" 等等的运作行为(method)。
========================================
Q11:什麽是物件(object)?
一块赋有某种语意的储存空间。
在宣告 "int i;" 之後,我们称「i 是个 int 型态的物件」。在 C++/OOP 里,「物
件」通常意指「类别的案例(an instance of a class)」,因此类别定义了数个物
件(案例)的行为。
========================================
Q12:什麽是参考(reference)?
一个物件的“别名”(alias,另一个名称)。
参考通常用於传址呼叫(pass-by-reference):
void swap(int& i, int& j)
{
int tmp = i;
i = j;
j = tmp;
}
main()
{
int x, y;
//...
swap(x,y);
}
在这里 "i" 和 "j" 分别是是 main 函数中 "x" 与 "y" 的别名,换句话说,"i" 就
是 "x"--不是个指向 "x" 的指标,也不是 "x" 该值的复制品,而它的的确确就是
"x" 本身。你对 "i" 做的任何动作,都会反映到 "x" 上;反之亦然。
从最底层来看,参考最常用指标来实作,它的效果有点像 C 里头的「传指标呼叫」
(pass-by-pointer),但 "&" 取址运算子由呼叫者换到被呼叫者之处了,你也要删
去所有的 "*" 运算子。
========================================
Q13:如果设定某值给参考会怎麽样?
会更动到被参考者(referrent,该「参考」所参考到的物件)。
记住:「参考」就是「被参考者」,因此动了参考就会改动到被参考者(「参考」是
「被参考者」的左值 "Lvalue"〔出现在设定陈述的左边〕)。
更进一步,我们也允许参考被传回。这样子函数呼叫就可放在设定陈述的左边,这对
运算子多载的场合很有用。
========================================
Q14:怎样才能将参考改设成别的物件?
没有办法。
和指标不同,一旦参考被系结到某个物件,它就不能再被改设到其他物件去。「参考
」本身不是一个物件(它自己没有位址;「取参考的位址」只会得到被参考者的位址
;切记:「参考」就是「被参考者」)。
将「参考」与「被参考者」分离开来是不可能的。
========================================
Q15:何时该用参考,何时又该用指标?
可以时,用参考;必要时,就用指标。
当你不需要“重设”它时(见前一个问题),参考会比指标好。这通常意味著:在物
件类别的公共介面中参考最有用。参考大多用於物件的表层,而指标则多用於里层。
但有一个例外:当函数参数或传回值需要一个「临界」(sentinel)的参考值时,最
好是用指标来做,以 NULL 指标做为一个特别值(「参考」应该是个实质物件的「别
名」,而不是个解参用的〔dereferenced〕NULL 指标)。
注意:老资格的 C 程式员不喜欢参考,因为在父程式的地方,「参考」的语意并不
是那麽明显。然而有了些 C++经验後,会发现这正是一种「资讯隐藏」的作法,是利
而非弊。好比说,程式员应该以切近该问题的方式来写程式,而非以机器的语言来解
题。
========================================
Q16:行内函数是做什麽的?
行内函数(inline function)是个程式码会塞入呼叫者所在之处的函数。就像巨集
一样,行内函数免除了函数呼叫的额外负担,以增进效率,并且(尤其是!)还能让
编译器对它施以最佳化(程序融合 "procedural integration")。不过和巨集不同
的是:它只会对所有引数求一次的值(在语意上,该“函数呼叫”和正常函数一样,
只是比较快速罢了),以避免某些不易察觉的巨集错误。此外,它还会检测引数的型
态,做必要的型别转换(巨集对你有害;除非绝对必要,否则别再用它了)。
注意:过度使用行内函数会让程式码肥胖,於分页(paging)环境下反而有负面的性
能影响。
宣告法:在函数定义处使用 "inline" 关键字:
inline void f(int i, char c) { /*...*/ }
或者是在类别内将定义包括进去:
class Fred {
public:
void f(int i, char c) { /*...*/ }
};
或是在类别外头,以 "inline" 来定义该成员函数:
class Fred {
public:
void f(int i, char c);
};
inline void Fred::f(int i, char c) { /*...*/ }
=============================
■□ 第5节:建构子和解构子
=============================
Q17:建构子(constructor)是做什麽的?
建构子乃用来从零开始建立物件。
建构子就像个「初始化函数」;它把一堆散乱的位元组成一个活生生的物件。最低限
度它会初始化内部用到的栏位,也可能会配置所须的资源(记忆体、档案、semaphore
、socket 等等)。
"ctor" 是建构子 constructor 最常见的缩写。
========================================
Q18:怎样才能让建构子呼叫另一个同处一室的建构子?
没有办法。
原因是:如果你呼叫另一个建构子,编译器会初始化一个暂时的区域性物件;但并没
有初始化“这个”你想要的物件。你可以用预设参数(default parameter),将两
个建构子合并起来,或是在私有的 "init()" 成员函数中共享它们的程式码。
========================================
Q19:解构子(destructor)是做什麽的?
解构子乃物件之葬礼。
解构子是用来释放该物件所配置到的资源,譬如:Lock 类别可能会锁住一个
semaphore,解构子则用来释放它。最常见的例子是:当建构子用了 "new" 以後,解
构子用 "delete"。
解构子是个「去死吧」的运作行为(method),通常缩写为 "dtor"。
=========================
■□ 第6节:运算子多载
=========================
Q20:运算子多载(operator overloading)是做什麽的?
它可让使用类别的人以直觉来操作之。
运算子多载让 C/C++ 的运算子,能对自订的型态(物件类别)赋予自订的意义。它
们形同是函数呼叫的语法糖衣 (syntactic sugar):
class Fred {
public:
//...
};
#if 0
Fred add(Fred, Fred); //没有运算子多载
Fred mul(Fred, Fred);
#else
Fred operator+(Fred, Fred); //有运算子多载
Fred operator*(Fred, Fred);
#endif
Fred f(Fred a, Fred b, Fred c)
{
#if 0
return add(add(mul(a,b), mul(b,c)), mul(c,a)); //没有...
#else
return a*b + b*c + c*a; //有...
#endif
}
========================================
Q21:哪些运算子可以/不能被多载?
大部份都可以被多载。
不能的 C 运算子有 "." 和 "?:"(和以技术上来说,可算是运算子的 "sizeof")。
C++ 增加了些自己的运算子,其中除了 "::" 和 ".*". 之外都可以被多载。
底下是个足标(subscript)运算子的例子(它会传回一个参考)。最前面是“不用
”多载的:
class Array {
public:
#if 0
int& elem(unsigned i) { if (i>99) error(); return data[i]; }
#else
int& operator[] (unsigned i) { if (i>99) error(); return data[i]; }
#endif
private:
int data[100];
};
main()
{
Array a;
#if 0
a.elem(10) = 42;
a.elem(12) += a.elem(13);
#else
a[10] = 42;
a[12] += a[13];
#endif
}
========================================
Q22:怎样做一个 "**"「次方」运算子?
无解。
运算子的名称、优先序、结合律以及元数(arity)都被语言所定死了。C++ 里没有
"**" 运算子,所以你无法替类别订做一个它。
还怀疑的话,考虑看看 "x ** y" 和 "x * (*y)",这两者是完全一样的(换句话说
,编译器会假设 "y" 是个指标)。此外,运算子多载只是函数呼叫的语法糖衣而已
,虽然甜甜的,但本质上并未增加什麽东西。我建议你多载 "pow(base,exponent)"
这个函数(它的倍精确度版本在 <math.h> 中)。
附带一提:operator^ 可以用,但它的优先序及结合律不符「次方」所需。
===================
■□ 第7节:夥伴
===================
Q23:夥伴(friend)是什麽?
让别的类别或函数能存取到你的类别内部的东西。
夥伴可以是函数或其他类别。类别会对它的夥伴开放存取权限。正常情况下,程式员
会下意识、技术性地控制该类别的夥伴与运作行为(否则当你想更动类别时,还得先
有其他部份的拥有者之同意才行)。
========================================
Q24:「夥伴」违反了封装性吗?
若善用之,反而会「强化」封装性。
我们经常得将一个类别切成两半,当这两半各有不同的案例个数及生命期时。在此情
形之下,它们通常需要直接存取对方的内部(这两半“本来”是在同一个类别里面,
所以你并未“增加”存取资料结构的运作行为个数;你只是在“搬动”这些运作行为
所在之处而已)。最安全的实作方式,就是让这两半互为彼此的「夥伴」。
若你如上述般的使用夥伴,你依然是将私有的东西保持在私有的状态。遇到上述的情
况,如果还呆呆的想避免使用夥伴关系,许多人不是采用公共资料(糟透了!),就
是弄个公共的 get/set 存取运作行为来存取彼此的资料,事实上这些都破坏了封装
性。只有在类别的外面该私有资料「仍有其意义」(以使用者的角度来看)时,开放
出私有资料的存取运作行为才称得上是恰当的做法。多数情况下,「存取运作行为」
就和「公共资料」一样糟糕:它们对私有资料成员只隐其“名”而已,却未隐藏其“
存在”。
同样的,如果将「夥伴函数」做为另一种类别公共存取函数的语法,那就和违反封装
性的成员函数一样破坏了封装。换句话说,物件类别的夥伴及成员都是「封装的界线
」,如同「类别定义」本身一样。
========================================
Q25:夥伴函数的优缺点?
它提供了某种介面设计上的自由。
成员函数和夥伴函数都有同等的存取特权(100% 的权利),主要的差别在於:夥伴
函数用起来像是 "f(x)",而成员函数则是 "x.f()"。因此,夥伴函数可让物件类别
设计者挑选他看得最顺眼的语法,以降低维护成本。
夥伴函数主要的缺点在於:当你想做动态系结(dynamic binding)时,它需要额外
的程式码。想做出「虚拟夥伴」的效果,该夥伴函数应该呼叫个隐藏的(通常是放在
"protected:" 里)虚拟成员函数;像这个样子:"void f(Base& b) { b.do_f(); }"
。衍生类别会覆盖(override)掉那个隐藏的成员函数("void Derived::do_f()")
,而不是该夥伴函数。
========================================
Q26:「夥伴关系无继承及递移性」是什麽意思?
夥伴关系的特权性无法被继承下来:夥伴的衍生类别不必然还是夥伴(我把你当朋友
,但这不代表我也一定会信任你的孩子)。如果 "Base" 类别宣告了 "f()" 为它的
夥伴,"f()" 并不会自动对由 "Base" 衍生出来的 "Derived" 类别所多出来的部份
拥有特殊的存取权力。
夥伴关系的特权无递移性:夥伴类别的夥伴不必然还是原类别的夥伴(朋友的朋友不
一定也是朋友)。譬如,如果 "Fred" 类别宣告了 "Wilma" 类别为它的夥伴,而且
"Wilma" 类别宣告了 "f()" 为它的夥伴,则 "f()" 不见得对 "Fred" 有特殊的存取
权力。
========================================
Q27:应该替类别宣告个成员函数,还是夥伴函数?
可能的话,用成员函数;必要时,就用夥伴。
有时在语法上来看,夥伴比较好(譬如:在 "Fred" 类别中,夥伴函数可把 "Fred"
弄成是第二个参数,但在成员函数中则一定得放在第一个)。另一个好例子是:二元
中序式算数运算子(譬如:"aComplex + aComplex" 可能应该定义成夥伴而非成员函
数,因为你想让 "aFloat + aComplex" 这种写法也能成立;回想一下:成员函数无
法提升它左侧的参数,因为那样会把引发该成员函数的物件所属之类别给改变掉)。
在其他情况下,请选成员函数而不要用夥伴函数。
====================================================
■□ 第8节:输入/输出:<iostream.h> 和 <stdio.h>
====================================================
Q28:该怎样替 "class Fred" 提供输出功能?
用夥伴函数 operator<<:
class Fred {
public:
friend ostream& operator<< (ostream& o, const Fred& fred)
{ return o << fred.i; }
//...
private:
int i; //只为了说明起见而设的
};
我们用夥伴而不用成员函数,因为 "Fred" 是第二个参数而非第一个。输入的功能亦
类似,只是要改写成:
istream& operator>> (istream& i, Fred& fred);
// ^^^^^------- 不是 "const Fred& fred"!
========================================
Q29:为什麽我该用 <iostream.h> 而不是以前的 <stdio.h>?
增加型别安全、减少错误、增进效率、有延展性、提供衍生能力。
Printf 还好,而 scanf 除了容易写错之外也还算可以,然而和 C++ 的 I/O 系统相
比,它们都有其限制。C++ 的 I/O(用 "<<" 及 ">>" ),和 C( "printf()" 和
"scanf()" )相比:
* 型别安全--要做 I/O 的物件,编译器会静态地事先得知其型别,而不是动态地
由 "%" 一栏查知。
* 不易出错--冗馀的资讯会增加错误的机会。C++ 的 I/O 就不需要多馀的 "%"。
* 更快速--printf 是个小型语言的「解译器」,该语言主要是由 "%" 这种东西
构成的;在执行期它用这些栏位来选择正确的格式化方式。C++ 的 I/O 系统则是
静态的依各引数真正的型别来挑选副程式,以增进执行效率。
* 延展性--C++ I/O 机制可在不改动原有程式码的情况下,就加进使用者新设计
的型态(能想像如果大家同时把互不相容的 "%" 栏位塞入 printf 和 scanf,会
是怎样的混乱场面?!)。
* 可衍生(subclassable)--ostream 和 istream(C++ 的 FILE* 代替品)都是
真正的类别,因此可以被衍生下去。这意味著:你可以让其他自定的东西有著和
stream 雷同的外表与行为,但实际上做的却是你想做的特定事情。你自动就重用
了数以万计别人(你甚至不认识它们)写好的 I/O 程式码,而他们也不需要知道
你所做的「延伸 stream」类别。
========================================
Q30:为什麽我处理输入时,会超过档案的结尾?
因为 eof(档案结尾)的状态,是到「将要超过档案结尾的动作」才会被设定。也就
是说,读档案的最後一个位元组并不会设定 eof 的状态。
【译注】这也是 C 常见的错误。
如果你的程式像这样:
int i = 0;
while (! cin.eof()) {
cin >> x;
++i;
// work with x
}
你的 i 变数就会多了一。
你真正该做的是这样:
int i;
while (cin >> x) {
++i;
// work with x
}
========================================
Q31:为什麽我的程式执行完第一次回圈後,会对输入的要求不加理睬?
因为读取数值的程式,把非数字的字元留在输入缓冲区 (input buffer) 里头了。
【译注】这也是 C,甚至 Pascal 常见的错误。
如果你的程式如下:
char name[1000];
int age;
for (;;) {
cout << "Name: ";
cin >> name;
cout << "Age: ";
cin >> age;
}
你应该这样写:
for (;;) {
cout << "Name: ";
cin >> name;
cout << "Age: ";
cin >> age;
cin.ignore(INT_MAX, '\n');
}
========================================
Q32:在 DOS 及 OS/2 的 binary 模式下,要怎样来 "reopen" cin 及 cout?
有这个问题,最典型的情况就是:有人想对 cin、cout 做 binary 的 I/O,但是作
业系统(像是 DOS 或 OS/2)却总是会做 CR-LF 的转换动作。
解决法:cin、cout、cerr 这些事先定义好的串流,都是 text 的串流,没有标准做
法能把它们弄成 binary 模式。把串流关掉再设法以 binary 模式 reopen 它们,可
能会导致不可预期的结果。
在这两种模式有不同行为的系统上,一定有办法让它们变成 binary 串流,但是你得
去查查该系统的文件。
--
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
--
m;32m※ 转寄:.华南网木棉站 bbs.gznet.edu.cn.[FROM: mtlab.hit.edu.cn]
--
Enjoy Linux!
-----It's FREE!-----
※ 来源:.紫 丁 香 bbs.hit.edu.cn.[FROM: mtlab.hit.edu.cn]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:615.522毫秒