注册 | 登录
收藏 | 帮助
热门文章
编辑推荐
相关文章  
模块复用——c++类、dll和com
获取ACCESS2000数据库中的所有表
C++语言常见问题解答(2)
C++语言常见问题解答(3)
C++语言常见问题解答(4)
在c++程序中重启自己的一种方法
您现在的位置: 顶尖设计 >> IT学院 >> 编程开发 >> C >> 文章正文
C++语言常见问题解答(1)
作者:中译者:叶秉哲  来源:永远的UNIX  点击:  更新:2006-12-19
简介:
行吗? 
 
有的;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)" 
这个函数(它的倍精确度版本在  中)。 
 
附带一提: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:「夥伴关系无继承及递移性」是什麽意思? 
 
夥伴关系的特权性无法被继承下

上一页  [1] [2] [3] [4] [5] 下一页






  • 上一篇文章:
  • 下一篇文章:
  • 分享此文:该页面添加到 Mister Wong 添加到雅虎Yahoo!收藏 Add to:Del.icio.us Post to Furl Digg this 添加到Google书签 reddit spurl blogmarks 365Key 评论  收藏  分享  打印
     我来说两句
    姓名:       验证码:   
    主页: 
    评分: 1分 2分 3分 4分 5分
    本频道近期热评文章:
      关于我们 | 联系我们 | 站点地图 | 广告投放 | 友情链接 | 在线留言 | 版权申明
    版权所有 © 2004-2007 顶尖设计(bobd.cn)
    未经授权禁止转载,摘编,复制本站内容或建立镜像. 沪ICP备07504942号 
    网络110
    报警服务