📄 00000001.htm
字号:
<BR>======================================== <BR> <BR>Q42:怎样确保某类别的物件都是用 "new" 建立的,而非区域或整体/静态变数? <BR> <BR>确定该类别的建构子都是 "private:" 的,并定义个 "friend" 或 "static" 函数, <BR>来传回一个指向由 "new" 建造出来的物件(把建构子设成 "protected:",如果你想 <BR>要有衍生类别的话)。 <BR> <BR> class Fred { //只允许 Fred 动态地配置出来 <BR> public: <BR> static Fred* create() { return new Fred(); } <BR> static Fred* create(int i) { return new Fred(i); } <BR> static Fred* create(const Fred& fred) { return new Fred(fred); } <BR> private: <BR> Fred(); <BR> Fred(int i); <BR> Fred(const Fred& fred); <BR> virtual ~Fred(); <BR> }; <BR> <BR> main() <BR> { <BR> Fred* p = Fred::create(5); <BR> ... <BR> delete p; <BR> } <BR> <BR> <BR>=============================== <BR>■□ 第10节:除错与错误处理 <BR>=============================== <BR> <BR>Q43:怎样处理建构子的错误? <BR> <BR>丢出一个例外(throw an exception)。 <BR> <BR>建构子没有传回值,所以不可能采用它传回的错误码。因此,侦测建构子错误最好的 <BR>方法,就是丢出一个例外。 <BR> <BR>在 C++ 编译器尚未提供例外处理之前,我们可先把物件置於「半熟」的状态(譬如 <BR>:设个内部的状态位元),用个查询子("inspector")来检查该位元,就可让用户 <BR>查看该物件是否还活著。也可以用另一个成员函数来检查该位元,若该物件没存活 <BR>下来,就做个「没动作」(或是更狠的像是 "abort()" )的程式。但这实在很丑陋。 <BR> <BR>======================================== <BR> <BR>Q44:如果建构子会丢出例外的话,该怎麽处理它的资源? <BR> <BR>物件里面的每个资料成员,都该自己收拾残局。 <BR> <BR>如果建构子丢出一个例外的话,该物件的解构子就“不会”执行。如果你的物件得回 <BR>复些曾做过的事情(像是配置记忆体、开启档案、锁定 semaphore),该物件内的资 <BR>料成员就“必须”记住这个「必须恢复的东西」。 <BR> <BR>举例来说:不要单单的把配置到的记忆体放入 "Fred*" 资料成员,而要放入一个「 <BR>聪明的指标」(smart pointer) 资料成员中;当该“聪明指标”死掉的话,它的解构 <BR>子就会删去 Fred 物件。 <BR> <BR>【译注】「聪明的指标」(smart pointer) 在 Q4 中有提到一点。 <BR> <BR> <BR>============================= <BR>■□ 第11节:Const 正确性 <BR>============================= <BR> <BR>Q45:什麽是 "const correctness"? <BR> <BR>好问题。 <BR> <BR>「常数正确性」乃使用 "const" 关键字,以确保常数物件不会被更动到。譬如:若 <BR>"f()" 函数接收一个 "String",且 "f()" 想确保 "String" 不会被改变,你可以: <BR> <BR> * 传值呼叫 (pass by value): void f( String s ) { /*...*/ } <BR> * 透过常数参考 (reference): void f(const String& s ) { /*...*/ } <BR> * 透过常数指标 (pointer) : void f(const String* sptr) { /*...*/ } <BR> * 但不能用非常数参考 : void f( String& s ) { /*...*/ } <BR> * 也不能用非常数指标 : void f( String* sptr) { /*...*/ } <BR> <BR>在接收 "const String&" 参数的函数里面,想更动到 "s" 的话,会产生个编译期的 <BR>错误;没有牺牲任何执行期的空间及速度。 <BR> <BR>宣告 "const" 参数也是另一种型别安全方法,就像一个常数字串,它会“丧失”各 <BR>种可能会变更其内容的行为动作。如果你发现型别安全性质让你的系统正确地运作 <BR>(这是真的;特别是大型的系统),你会发现「常数正确性」亦如是。 <BR> <BR>======================================== <BR> <BR>Q46:我该早一点还是晚一点让东西有常数正确性? <BR> <BR>越越越早越好。 <BR> <BR>延後补以常数正确性,会导致雪球效应:每次你在「这儿」用了 "const",你就得在 <BR>「那儿」加上四个以上的 "const"。 <BR> <BR>======================================== <BR> <BR>Q47:什麽是「const 成员函数」? <BR> <BR>一个只检测(而不更动)其物件的成员函数。 <BR> <BR> class Fred { <BR> public: <BR> void f() const; <BR> }; // ^^^^^--- 暗示说 "fred.f()" 不会改变到 "fred" <BR> <BR>此乃意指:「抽象层次」的(用户可见的)物件状态不被改变(而不是许诺:该物件 <BR>的「每一个位元内容」都不会被动到)。C++ 编译器不会对你许诺「每一个位元」这 <BR>种事情,因为不是常数的别名(alias)就可能会修改物件的状态(把 "const" 指标 <BR>黏上某个物件,并不能担保该物件不被改变;它只能担保该物件不会「被该指标的动 <BR>作」所改变)。 <BR> <BR>【译注】请逐字细读上面这句话。 <BR> <BR>"const" 成员函数常被称作「查询子」(inspector),不是 "const" 的成员函数则 <BR>称为「更动子」(mutator)。 <BR> <BR>======================================== <BR> <BR>Q48:若我想在 "const" 成员函数内更新一个「看不见的」资料成员,该怎麽做? <BR> <BR>使用 "mutable" 或是 "const_cast"。 <BR>【译注】这是很新的 ANSI C++ RTTI (RunTime Type Information) 规定,Borland <BR> C++ 4.0 就率先提供了 const_cast 运算子。 <BR> <BR>少数的查询子需要对资料成员做些无害的改变(譬如:"Set" 物件可能想快取它上一 <BR>回所查到的东西,以加速下一次的查询)。此改变「无害」是指:此改变不会由物件 <BR>的外部介面察觉出来(否则,该运作行为就该叫做更动子,而非查询子了)。 <BR> <BR>这类情况下,会被更动的资料成员就该被标示成 "mutable"(把 "mutable" 关键字 <BR>放在该资料成员宣告处前面;也就是和你放 "const" 一样的地方),这会告诉编译 <BR>器:此资料成员允许 const 成员函数改变之。若你不能用 "mutable" 的话,可以用 <BR>"const_cast" 把 "this" 的「常数性」给转型掉。譬如,在 "Set::lookup() const" <BR>里,你可以说: <BR> <BR> Set* self = const_cast<Set*>(this); <BR> <BR>这行执行之後,"self" 的位元内容就和 "this" 一样(譬如:"self==this"),但 <BR>是 "self" 是一个 "Set*" 而非 "const Set*" 了,所以你就可以用 "self" 去修改 <BR>"this" 指标所指向的物件。 <BR> <BR>======================================== <BR> <BR>Q49:"const_cast" 会不会丧失最佳化的可能? <BR> <BR>理论上,是;实际上,否。 <BR> <BR>就算编译器没真正做好 "const_cast",欲避免 "const" 成员函数被呼叫时,会造成 <BR>暂存器快取区被清空的唯一方法,乃确保没有任何「非常数」的指标指向该物件。这 <BR>种情况很难得会发生(当物件在 const 成员函数被启用的□围内被建立出来;当所 <BR>有非 const 的成员函数在物件建立间启用,和 const 成员函数的启用被静态系结住 <BR>;当所有的启用也都是 "inline";当建构子本身就是 "inline";和当建构子所呼叫 <BR>的任何成员函数都是 inline 时)。 <BR> <BR>【译注】这一段话很难翻得好(好啦好啦!我功力不足... :-< ),所以附上原文: <BR>Even if a compiler outlawed "const_cast", the only way to avoid flushing <BR>the register cache across a "const" member function call would be to <BR>ensure that there are no non-const pointers that alias the object. This <BR>can only happen in rare cases (when the object is constructed in the scope <BR>of the const member fn invocation, and when all the non-const member <BR>function invocations between the object's construction and the const <BR>member fn invocation are statically bound, and when every one of these <BR>invocations is also "inline"d, and when the constructor itself is "inline"d, <BR>and when any member fns the constructor calls are inline). <BR> <BR> <BR>===================== <BR>■□ 第12节:继承 <BR>===================== <BR> <BR>Q50:「继承」对 C++ 来说很重要吗? <BR> <BR>是的。 <BR> <BR>「继承」是抽象化资料型态(abstract data type, ADT)与 OOP 的一大分野。 <BR> <BR>======================================== <BR> <BR>Q51:何时该用继承? <BR> <BR>做为一个「特异化」(specialization) 的机制。 <BR> <BR>人类以两种角度来抽象化事物:「部份」(part-of) 和「种类」(kind-of)。福特汽 <BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -