📄 00000003.htm
字号:
Q110:为什麽我总觉得 C++ 让我「离机器更远了」,不像 C 那样? <BR> <BR>因为事实上正是如此。 <BR> <BR>做为一个 OOPL,C++ 让你以该问题的领域来思考,让你以问题领域的语言来设计程 <BR>式,而非以解题的领域来著手。 <BR> <BR>一个 C 最强的地方是:它没有「隐藏的机制」:你看到的就是你得到的,你可以一 <BR>边阅读 C 的程式,一边「看到」每个系统时脉。C++ 则不然; C 的老手(像从前的 <BR>我们)对这种特性常会有矛盾的心理(或是说「敌视」),但是很快的他们会发现: <BR>C++ 提供了抽象化的层次及经济的表现能力,大大降低维护成本,又不会损及执行效 <BR>率。 <BR> <BR>很自然的,用任何语言都会写出坏程式;C++ 并不会确保任何高品质、可重用性、抽 <BR>象化,或是任何「正字标记」的品质因子。C++ 不会让差劲的程式者写不出差劲的程 <BR>式;她只是协助明智的发展者做出高人一等的软体。 <BR> <BR> <BR>=================================== <BR>■□ 第18节:指向成员函数的指标 <BR>=================================== <BR> <BR>Q111:「指向成员函数的指标」和「指到函数的指标」的型态有差别吗? <BR> <BR>是的。 <BR> <BR>考虑底下的函数: <BR> <BR> int f(char a, float b); <BR> <BR>如果它是普通的函数,它的型态是: int (*) (char,float); <BR>如果它是 Fred 类别的运作行为,它的型态是: int (Fred::*)(char,float); <BR> <BR>======================================== <BR> <BR>Q112:怎样把指向成员函数的指标传给 signal handler、X event callback 等等? <BR> <BR>【译注】这是和 UNIX、X Window System 相关的问题,但其他系统亦可推而广之。 <BR> <BR>不要这样做。 <BR> <BR>因为若无物件去启动它,成员函数是无意义的,你不能直接使用它(如果 X 视窗系 <BR>统是用 C++ 写的话,或许就可以直接传物件的参考值了,而不光是传个指向函数的 <BR>指标;自然地,物件会包含所有要用到的函数,甚至更多)。 <BR> <BR>若想修改现有的软体,可拿最顶层的(非成员的)函数当作一层包装 (wrapper),透 <BR>过其他技巧(或许是放在全域变数中),把该物件包起来。这个最顶层的函数,会透 <BR>过适当的成员函数去使用该全域变数。 <BR> <BR>譬如,你想在中断处理中呼叫 Fred::memfn() 的话: <BR> <BR> class Fred { <BR> public: <BR> void memfn(); <BR> static void staticmemfn(); // 用个 static 成员函数就行了 <BR> //... <BR> }; <BR> <BR> //wrapper 函数会记得哪个物件该去启动全域物件的成员函数: <BR> Fred* object_which_will_handle_signal; <BR> void Fred_memfn_wrapper() { object_which_will_handle_signal->memfn(); } <BR> <BR> main() <BR> { <BR> /* signal(SIGINT, Fred::memfn); */ //不能这样做 <BR> signal(SIGINT, Fred_memfn_wrapper); //Ok <BR> signal(SIGINT, Fred::staticmemfn); //Also Ok <BR> } <BR> <BR>注意:静态成员函数不需要真正的物件才能启动,所以指向静态成员函数的指标,和 <BR>普通的指向函数的指标,具有相容的型态(详见 ARM ["Annotated Reference <BR>Manual"] p.25, 158)。 <BR> <BR>======================================== <BR> <BR>Q113:当我想以成员函数做为中断服务常式 (ISR) 时,为什麽编译器产生(型态不 <BR> 符)的错误? <BR> <BR>这是前两个问题的特例,所以请先看看前两则解答。 <BR> <BR>非静态的成员函数,都有一个隐藏的参数,对应到 'this' 指标,该 'this' 指标会 <BR>指向该物件的案例资料 (instance data),可是系统中断的硬体/韧体并未提供这个 <BR>'this' 参数。你得用「正常的」函数(不是类别的成员)或是静态成员函数来做为 <BR>中断服务常式才行。 <BR> <BR>一个可行的解法是:用一个静态成员做为中断服务常式,让它能自己到某处去找案例 <BR>/成员的配对,以供中断呼叫之用。这麽一来,当中断产生时,正常的 method 就会 <BR>被启动,不过以技术观点来看,你得先呼叫一个中介函数。 <BR> <BR>======================================== <BR> <BR>Q114:为什麽我取不出 C++ 函数的位址? <BR> <BR>这可由前一则 FAQ 推论过来。 <BR> <BR>详细的解答:在 C++ 里,成员函数有一个隐含的参数,指向该物件本身(成员函数 <BR>内的 "this" 指标)。正常的 C 函数与成员函数的呼叫惯例可视为不同,所以它们 <BR>指标的型态(指向成员函数 vs 指向函数)既不同也不相容。C++ 引进一个新的指标 <BR>型态:指向成员的指标,要提供一个物件才能启动之(见 ARM ["Annotated <BR>Reference Manual"] 5.5)。 <BR> <BR>注意:不要去把指向成员函数的指标强制转型成指向函数的指标;这样做的结果是未 <BR>定义的,且下场可能会很惨。譬如,指向成员函数的指标,“不必然”会包含某正常 <BR>函数的机器位址(看 ARM, 8.1.2c, p.158)。如前例所提,如果你有个指向正常 C <BR>函数的指标的话,请用上层的(非成员的)函数,或是用 "static" 成员函数(类别 <BR>成员函数)。 <BR> <BR>======================================== <BR> <BR>Q115:怎样宣告指向成员函数的指标阵列? <BR> <BR>用 "typedef" 好让你的脑筋保持清醒。 <BR> <BR> class Fred { <BR> public: <BR> int f(char x, float y); <BR> int g(char x, float y); <BR> int h(char x, float y); <BR> int i(char x, float y); <BR> //... <BR> }; <BR> <BR> typedef int (Fred::*FredPtr)(char x, float y); <BR> <BR>这是指向成员函数的指标阵列:Here's the array of pointers to member functions: <BR> <BR> FredPtr a[4] = { &Fred::f, &Fred::g, &Fred::h, &Fred::i }; <BR> <BR>呼叫物件 "fred" 的某一个成员函数: <BR> <BR> void userCode(Fred& fred, int methodNum, char x, float y) <BR> { <BR> //假设 "methodNum" 在 [0,3] 区间内 <BR> (fred.*a[methodNum])(x, y); <BR> } <BR> <BR>你可以用 #define 让这个呼叫清楚些: <BR> <BR> #define callMethod(object,ptrToMethod) ((object).*(ptrToMethod)) <BR> callMethod(fred, a[methodNum]) (x, y); <BR> <BR> <BR>==================================== <BR>■□ 第19节:容器类别与 template <BR>==================================== <BR> <BR>Q116:怎样自一个连结串列/杂凑表等等里面,插入/存取/改变元素? <BR> <BR>我将以最简单的「插入连结串列」为例。想把元素插入串列的头尾很容易,但只限 <BR>於这些功能的话,会使程式库过於低能(太低能的程式库比没有更糟)。 <BR> <BR>完整的解答会让 C++ 新手消化不良,所以我只提几个项目。第一个是最简单的,第 <BR>二和第三是比较好的。 <BR> <BR>[1] 替 "List" 加入一个「现在位置」的性质,加入像是 advance()、backup()、 <BR> atEnd()、atBegin()、getCurrElem()、setCurrElem(Elem)、insertElem(Elem) <BR> 、removeElem() 等等的运作行为。 <BR> <BR> 即使在这个小例子里已经够用了,但「只有一个」现在位置的记号的话,想存取 <BR> 串列中两个以上位置的元素就不太容易(譬如:「对所有 x,y 序对,做底下的 <BR> 事情……」)。 <BR> <BR>[2] 把上述的 List 运作行为拿掉,移到独立的类别 "ListPosition" 中。 <BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -