[ 永遠的UNIX::UNIX技術資料的寶庫 ]   GB | BIG5

首頁 > 編程技術 > C/C++ > 正文
C++語言常見問題解答(4)
中譯者:葉秉哲 (2001-04-27 13:08:06)
== Part 4/4  ============================ 
======================================= 
■□ 第17節:和 C 連結/和 C 的關系 
======================================= 
 
Q105:怎樣從 C++ 中呼叫 C 的函數 "f(int,char,float)"? 
 
告訴 C++ 編譯器說:它是個 C 的函數: 
        extern "C" void f(int,char,float); 
 
確定你有 include 進來完整的函數原型 (function prototype)。一堆 C 的函數可 
以用大括號框起來,如下: 
 
        extern "C" { 
          void* malloc(size_t); 
          char* strcpy(char* dest, const char* src); 
          int   printf(const char* fmt, ...); 
        } 
 
======================================== 
 
Q106:怎樣才能建一個 C++ 函數 "f(int,char,float)",又能被 C 呼叫? 
 
想讓 C++ 編譯器知道 "f(int,char,float)" 會被 C 編譯器用到的話,就要用到前 
一則 FAQ 已詳述的 "extern C" 語法。接在 C++ 模組內定義該函數: 
 
        void f(int x, char y, float z) 
        { 
          //... 
        } 
 
"extern C" 一行會告訴編譯器:送到 linker 的外部資訊要採用 C 的呼叫慣例及簽 
名編碼法(譬如,前置一個底線)。既然 C 沒有多載名稱的能力,你就不能讓 C 程 
式能同時呼叫得到多載的函數群。 
 
警告以及實作相關事項: 
 * 你的 "main()" 應該用 C++ 編譯之(為了靜態物件的初始化)。 
 * 你的 C++ 編譯器應該能設定連結的程序(為某些特殊的程式庫)。 
 * 你的 C 和 C++ 編譯器可能要是同一個牌子的,而且是相容的版本(亦即:有相 
   同的呼叫慣例等等)。 
 
======================================== 
 
Q107:為什麼 linker 有這種錯誤訊息:C/C++ 函數被 C/C++ 函數呼叫到? 
 
看前兩則 FAQs 關於 extern "C" 的使用。 
 
======================================== 
 
Q108:該怎麼把 C++ 類別的物件傳給/傳自 C 的函數? 
 
例子: 
 
        /****** C/C++ header file: Fred.h ******/ 
        #ifdef __cplusplus    /*"__cplusplus" is #defined if/only-if 
                                 compiler is C++*/ 
          extern "C" { 
        #endif 
 
        #ifdef __STDC__ 
          extern void c_fn(struct Fred*);       /* ANSI-C prototypes */ 
          extern struct Fred* cplusplus_callback_fn(struct Fred*); 
        #else 
          extern void c_fn();                   /* K&R style */ 
          extern struct Fred* cplusplus_callback_fn(); 
        #endif 
 
        #ifdef __cplusplus 
          } 
        #endif 
 
        #ifdef __cplusplus 
          class Fred { 
          public: 
            Fred(); 
            void wilma(int); 
          private: 
            int a_; 
          }; 
        #endif 
 
"Fred.C" 是個 C++ 模組: 
 
        #include "Fred.h" 
        Fred::Fred() : a_(0) { } 
        void Fred::wilma(int a) : a_(a) { } 
 
        Fred* cplusplus_callback_fn(Fred* fred) 
        { 
          fred->wilma(123); 
          return fred; 
        } 
 
"main.C" 是個 C++ 模組: 
 
        #include "Fred.h" 
 
        int main() 
        { 
          Fred fred; 
          c_fn(&fred); 
          return 0; 
        } 
 
"c-fn.c" 是個 C 模組: 
 
        #include "Fred.h" 
        void c_fn(struct Fred* fred) 
        { 
          cplusplus_callback_fn(fred); 
        } 
 
把指向 C++ 物件的指標傳到/傳自 C 的函數,如果傳出與收回的指標不是“完全相 
同”的話,就會失敗。譬如,不要傳出一個基底類別的指標卻收回一個衍生類別的指 
標,因為 C 編譯器不懂該怎麼對多重及虛擬繼承的指標做轉型。 
 
======================================== 
 
Q109:C 的函數能不能存取 C++ 類別的物件資料? 
 
有時可以。 
 
(請先讀一讀前一則關於和 C 函數間傳遞 C++ 物件的 FAQ。) 
 
你可以安全地從 C 函數中存取 C++ 物件的資料,只要 C++ 的物件類別: 
 * 沒有虛擬函數(包含繼承下來的虛擬函數). 
 * 所有資料都在同一個存取等級中 (private/protected/public). 
 * 完全被包含的子物件中也都沒有虛擬函數. 
 
如果 C++ 類別有任何基底類別(或是任何被完全包含的子物件中有基底類別)的話 
,技術上來說,存取該資料沒有可攜性的,因為語言沒規定在繼承之下的類別配置是 
什麼樣子。不過經驗上,所有 C++ 編譯器的做法都一樣:基底類別物件先出現(在 
多重繼承之下,則由左到右排列之),子物件次之。 
 
還有,如果類別(或是任何基底類別)含有任何虛擬函數,你時常可以(但不是一直 
都可以)假設有一個 "void*" 出現在物件第一個虛擬函數之所在,或是在該物件的 
第一個 word 那裡。同樣的,語言對它也沒規定到,但這似乎是「大家」都採取的做 
法。 
 
如果該類別有任何虛擬基底類別,情況會更復雜而且更沒有可攜性。常見的做法是: 
讓物件最後才包含基底類別之物件 (V)(不管 "V" 在繼承階層中在哪兒出現),物 
件的其他部份則以正常的次序出現。每個有 V 這個虛擬基底類別的衍生類別,實際 
上都有個“指標”指向最後一個物件的 V 的部份。 
 
======================================== 
 
Q110:為什麼我總覺得 C++ 讓我「離機器更遠了」,不像 C 那樣? 
 
因為事實上正是如此。 
 
做為一個 OOPL,C++ 讓你以該問題的領域來思考,讓你以問題領域的語言來設計程 
式,而非以解題的領域來手。 
 
一個 C 最強的地方是:它沒有「隱藏的機制」:你看到的就是你得到的,你可以一 
邊閱讀 C 的程式,一邊「看到」每個系統時脈。C++ 則不然; C 的老手(像從前的 
我們)對這種特性常會有矛盾的心理(或是說「敵視」),但是很快的他們會發現: 
C++ 提供了抽象化的層次及經濟的表現能力,大大降低維護成本,又不會損及執行效 
率。 
 
很自然的,用任何語言都會寫出壞程式;C++ 並不會確保任何高品質、可重用性、抽 
象化,或是任何「正字標記」的品質因子。C++ 不會讓差勁的程式者寫不出差勁的程 
式;她只是協助明智的發展者做出高人一等的軟體。 
 
 
=================================== 
■□ 第18節:指向成員函數的指標 
=================================== 
 
Q111:「指向成員函數的指標」和「指到函數的指標」的型態有差別嗎? 
 
是的。 
 
考慮底下的函數: 
 
        int f(char a, float b); 
 
如果它是普通的函數,它的型態是:            int (*)      (char,float); 
如果它是 Fred 類別的運作行為,它的型態是:  int (Fred::*)(char,float); 
 
======================================== 
 
Q112:怎樣把指向成員函數的指標傳給 signal handler、X event callback 等等? 
 
【譯注】這是和 UNIX、X Window System 相關的問題,但其他系統亦可推而廣之。 
 
不要這樣做。 
 
因為若無物件去啟動它,成員函數是無意義的,你不能直接使用它(如果 X 視窗系 
統是用 C++ 寫的話,或許就可以直接傳物件的參考值了,而不光是傳個指向函數的 
指標;自然地,物件會包含所有要用到的函數,甚至更多)。 
 
若想修改現有的軟體,可拿最頂層的(非成員的)函數當作一層包裝 (wrapper),透 
過其他技巧(或許是放在全域變數中),把該物件包起來。這個最頂層的函數,會透 
過適當的成員函數去使用該全域變數。 
 
譬如,你想在中斷處理中呼叫 Fred::memfn() 的話: 
 
        class Fred { 
        public: 
          void memfn(); 
          static void staticmemfn();    // 用個 static 成員函數就行了 
          //... 
        }; 
 
        //wrapper 函數會記得哪個物件該去啟動全域物件的成員函數: 
        Fred* object_which_will_handle_signal; 
        void Fred_memfn_wrapper() { object_which_will_handle_signal->memfn(); } 
 
        main() 
        { 
          /* signal(SIGINT, Fred::memfn); */   //不能這樣做 
          signal(SIGINT, Fred_memfn_wrapper);  //Ok 
          signal(SIGINT, Fred::staticmemfn);   //Also Ok 
        } 
 
注意:靜態成員函數不需要真正的物件才能啟動,所以指向靜態成員函數的指標,和 
普通的指向函數的指標,具有相容的型態(詳見 ARM ["Annotated Reference 
Manual"] p.25, 158)。 
 
======================================== 
 
Q113:當我想以成員函數做為中斷服務常式 (ISR) 時,為什麼編譯器產生(型態不 
      符)的錯誤? 
 
這是前兩個問題的特例,所以請先看看前兩則解答。 
 
非靜態的成員函數,都有一個隱藏的參數,對應到 'this' 指標,該 'this' 指標會 
指向該物件的案例資料 (instance data),可是系統中斷的硬體/韌體並未提供這個 
'this' 參數。你得用「正常的」函數(不是類別的成員)或是靜態成員函數來做為 
中斷服務常式才行。 
 
一個可行的解法是:用一個靜態成員做為中斷服務常式,讓它能自己到某處去找案例 
/成員的配對,以供中斷呼叫之用。這麼一來,當中斷產生時,正常的 method 就會 
被啟動,不過以技術觀點來看,你得先呼叫一個中介函數。 
 
======================================== 
 
Q114:為什麼我取不出 C++ 函數的位址? 
 
這可由前一則 FAQ 推論過來。 
 
詳細的解答:在 C++ 裡,成員函數有一個隱含的參數,指向該物件本身(成員函數 
內的 "this" 指標)。正常的 C 函數與成員函數的呼叫慣例可視為不同,所以它們 
指標的型態(指向成員函數 vs 指向函數)既不同也不相容。C++ 引進一個新的指標 
型態:指向成員的指標,要提供一個物件才能啟動之(見 ARM ["Annotated 
Reference Manual"] 5.5)。 
 
注意:不要去把指向成員函數的指標強制轉型成指向函數的指標;這樣做的結果是未 
定義的,且下場可能會很慘。譬如,指向成員函數的指標,“不必然”會包含某正常 
函數的機器位址(看 ARM, 8.1.2c, p.158)。如前例所提,如果你有個指向正常 C 
函數的指標的話,請用上層的(非成員的)函數,或是用 "static" 成員函數(類別 
成員函數)。 
 
======================================== 
 
Q115:怎樣宣告指向成員函數的指標陣列? 
 
用 "typedef" 好讓你的腦筋保持清醒。 
 
        class Fred { 
        public: 
          int f(char x, float y); 
          int g(char x, float y); 
          int h(char x, float y); 
          int i(char x, float y); 
          //... 
        }; 
 
        typedef  int (Fred::*FredPtr)(char x, float y); 
 
這是指向成員函數的指標陣列:Here's the array of pointers to member functions: 
 
        FredPtr a[4] = { &Fred::f, &Fred::g, &Fred::h, &Fred::i }; 
 
呼叫物件 "fred" 的某一個成員函數: 
 
        void userCode(Fred& fred, int methodNum, char x, float y) 
        { 
          //假設 "methodNum" 在 [0,3] 區間內 
          (fred.*a[methodNum])(x, y); 
        } 
 
你可以用 #define 讓這個呼叫清楚些: 
 
        #define  callMethod(object,ptrToMethod)   ((object).*(ptrToMethod)) 
        callMethod(fred, a[methodNum]) (x, y); 
 
 
==================================== 
■□ 第19節:容器類別與 template 
==================================== 
 
Q116:怎樣自一個連結串列/雜湊表等等裡面,插入/存取/改變元素? 
 
我將以最簡單的「插入連結串列」為例。想把元素插入串列的頭尾很容易,但只限 
於這些功能的話,會使程式庫過於低能(太低能的程式庫比沒有更糟)。 
 
完整的解答會讓 C++ 新手消化不良,所以我只提幾個項目。第一個是最簡單的,第 
二和第三是比較好的。 
 
[1] 替 "List" 加入一個「現在位置」的性質,加入像是 advance()、backup()、 
    atEnd()、atBegin()、getCurrElem()、setCurrElem(Elem)、insertElem(Elem) 
    、removeElem() 等等的運作行為。 
 
    即使在這個小例子裡已經夠用了,但「只有一個」現在位置的記號的話,想存取 
    串列中兩個以上位置的元素就不太容易(譬如:「對所有 x,y 序對,做底下的 
    事情……」)。 
 
[2] 把上述的 List 運作行為拿掉,移到獨立的類別 "ListPosition" 中。 
 
    ListPosition 的作用是:代表 List 裡「現在的位置」,這樣就允許許多位置 
    並存於同一個串列中。ListPosition 是 List 的夥伴,所以 List 的內部可對 
    外界隱藏起來(否則 List 的內部就會被它的公共運作行為所公開)。注意: 
    ListPosition 可以把運算子多載起來,像是 advance()、backup(),因為運算 
    子多載只是正常運作行為的語法糖衣而已。 
 
[3] 把整個位置處理(iteration)當成是一個基元事件(atomic event),建一個 
    class template 去涵蓋該事件。 
 
    它不會在內部回圈中使用公共存取運作行為(它有可能是虛擬函數),所以效率 
    能增進。不幸的,你的應用軟體會多出些額外的二元碼,因為 template 是以空 
    間換取時間的。欲知詳情,請見 [Koenig, "Templates as interfaces," 
    JOOP, 4, 5 (Sept 91)], 以及 [Stroustrup, "The C++ Programming Language 
    Second Edition," under "Comparator"]. 
 
======================================== 
 
Q117:「樣版」(template)的用意是什麼? 
 
Template 本意是個壓餅模子,它把餅乾都壓成差不多一個樣子(雖然餅乾的原料不 
盡相同,但它們都有相同的基本外形)。同理,class template 是個樣版模子,用 
來描述如何將一系列的物件類別弄成同一個基本型式;而 function template 則是 
用以描述一系列看起來差不多的函數。 
 
Class template 常用於制造型別安全的容器(即使這僅止於「如何使用它」而已)。 
 
======================================== 
 
Q118:"function template" 的語法/語意是什麼? 
 
考慮底下這個交換兩個整數引數的函數: 
 
        void swap(int& x, int& y) 
        { 
          int tmp = x; 
          x = y; 
          y = tmp; 
        } 
 
假如我們想交換 float、long、String、Set 和 FileSystems,我們還得寫那些 
大致看起來都一樣、只有型態不同的程式碼,有夠煩人。這種不花腦筋的重復性工作 
,正是電腦的專長,於是我們想出了 function template: 
 
        template 
        void swap(T& x, T& y) 
        { 
          T tmp = x; 
          x = y; 
          y = tmp; 
        } 
 
每次我們以一組型別來使用 "swap()",編譯器會找到上面這定義,並造出另一個 
"template function" ,來當作它的「案例」(instantiation)。譬如: 
 
        main() 
        { 
          int    i,j;  /*...*/  swap(i,j);  // 案例化 "int"    的 swap 
          float  a,b;  /*...*/  swap(a,b);  // 案例化 "float"  的 swap 
          char   c,d;  /*...*/  swap(c,d);  // 案例化 "char"   的 swap 
          String s,t;  /*...*/  swap(s,t);  // 案例化 "String" 的 swap 
        } 
 
(注意:"template function" 是 "function template" 實體化之後的案例。) 
 
======================================== 
 
Q119:"class template" 的語法/語意是什麼? 
 
考慮像是個整數陣列的容器類別: 
 
        // 這會放在像是 "Array.h" 的標頭檔中 
        class Array { 
        public: 
          Array(int len=10)                  : len_(len), data_(new int[len]){} 
         ~Array()                            { delete [] data_; } 
          int len() const                    { return len_;     } 
          const int& operator[](int i) const { data_[check(i)]; } 
                int& operator[](int i)       { data_[check(i)]; } 
          Array(const Array&); 
          Array& operator= (const Array&); 
        private: 
          int  len_; 
          int* data_; 
          int  check(int i) const 
            { if (i < 0 || i >= len_) throw BoundsViol("Array", i, len_); 
              return i; } 
        }; 
 
如同前述的 "swap()" ,一再為 float、char、String、Array-of-String 等等來重 
復設計 Array 類別,是很煩人的。 
 
        // 這會放在像是 "Array.h" 的標頭檔中 
        template 
        class Array { 
        public: 
          Array(int len=10)                : len_(len), data_(new T[len]) { } 
         ~Array()                          { delete [] data_; } 
          int len() const                  { return len_;     } 
          const T& operator[](int i) const { data_[check(i)]; } 
                T& operator[](int i)       { data_[check(i)]; } 
          Array(const Array&); 
          Array& operator= (const Array&); 
        private: 
          int len_; 
          T*  data_; 
          int check(int i) const 
            { if (i < 0 || i >= len_) throw BoundsViol("Array", i, len_); 
              return i; } 
        }; 
 
不像 template function 那樣,template classes(案例化的 class template)必 
須將那些用來案例化的參數型態明示出來: 
 
        main() 
        { 
          Array           ai; 
          Array         af; 
          Array         ac; 
          Array        as; 
          Array< Array >  aai; 
        }              // ^^^-- 注意這空格;不要用 "Array>" 
                       //       (編譯器會把 ">>" 看成單一的元素) 
 
======================================== 
 
Q120:什麼是「參數化型別」(parameterized type)? 
 
另一種 "class template" 的說法。 
 
「參數化型別」是一種型別,它被另一個型別或數值所參數化(parameterized)了。 
像 List 是一個型別 ("List") ,它被另一個型別 ("int") 所參數化。 
 
======================================== 
 
Q121:「泛型」(genericity)是什麼? 
 
另一種 "class template" 的說法。 
 
不要和「一般化」(generality,指不要過於特定的解題)弄混了,「泛型」指的是 
class template。 
 
 
======================= 
■□ 第20節:程式庫 
======================= 
 
Q122:怎樣拿到 "STL"? 
 
"STL" 代表 "Standard Templates Library",標準模版程式庫。取得法: 
 
STL HP official site:   ftp://butler.hpl.hp.com/stl 
STL code alternate:     ftp://ftp.cs.rpi.edu/stl 
STL code + examples:    http://www.cs.rpi.edu/~musser/stl.html 
 
STL hacks for GCC-2.6.3 已經在 GNU libg++ 2.6.2.1 或更新版本裡了(可能較早 
的版本也有)。多謝 Mike Lindner。 
 
======================================== 
 
Q123:怎樣 ftp 到 "Numerical Recipes" 附的程式? 
 
它是用賣的,把它放到網路上散布是違法的。不過它只需 $30 美元而已。 
 
======================================== 
 
Q124:為什麼我的執行檔會這麼大? 
 
很多人對這麼大的執行檔感到驚訝,特別是當原始碼只有一點點而已。例如一個簡單 
的 "hello world" 程式居然會產生大家都想不到的大小(40+K bytes)。 
 
一個原因是:有些 C++ 執行期程式庫被連結進去了。有多少被連結進去,就要看看 
你用到多少,以及編譯器把程式庫切割成多少塊而定。例如,iostream 很大,包含 
一大堆類別及虛擬函數,即使你只用到一點點,因為各元件之間的交互參考依存關系 
,可能會把整個 iostream 程式碼都塞進來了。(【譯注】如果 linker 做得好的話 
,應該能把完全用不到的元件 object code 砍掉,不隨之塞入你的執行檔中。) 
 
不要用靜態的,改用動態連結的程式庫版本,就可以使你的程式變小。 
 
欲知詳情,請看看你的編譯器手冊,或是尋求廠商的技術支援。 
 
 
=============================== 
■□ 第21節:特定系統的細節 
=============================== 
 
Q125:GNU C++ (g++) 把小程式造出大大的執行檔,為什麼? 
 
libg++(g++ 用到的程式庫)可能在編譯時帶有除錯的資訊(-g)。有些機器上,不 
帶除錯資訊地重新編譯它,會省下很大的磁碟空間(∼1 MB;缺點是:不能追蹤到 
libg++ 的呼叫)。僅僅 "strip" 掉執行檔,比不上先用 -g 重新編譯,再 "strip" 
掉 a.out 檔來得有效。 
 
用 "size a.out" 來看看執行碼的程式與資料區段到底佔了多大空間,而不要用 
"ls -s a.out" 這種包括了符號表格(symbol table)的方式。 
 
======================================== 
 
Q126:有 YACC 的 C++ 文法嗎? 
 
Jim Roskind 是 C++ 的 YACC 文法作者,它大體上和部份 USL cfront 2.0 所實作 
出來的語言相容(沒有 template、例外、執行期型態識別功能)。這份文法有些地 
方和 C++有細小而微妙的差別。 
 
它可用 anonymous ftp 到下列地方取得: 
 * ics.uci.edu (128.195.1.1) in "gnu/c++grammar2.0.tar.Z". 
 * mach1.npac.syr.edu (128.230.7.14) in "pub/C++/c++grammar2.0.tar.Z". 
 
======================================== 
 
Q127:什麼是 C++ 1.2?  2.0?  2.1?  3.0? 
 
這些不是“語言”的版本,而是 cfront 這個由 AT&T 做出來的、最早的 C++轉譯程 
式的版本編號。以這編號來“代表”C++ 語言的演進,已經是公認的慣例了。 
 
“非常”粗略地講,主要的特徵有: 
 * 2.0 包含多重/虛擬繼承,以及純虛擬函數。 
 * 2.1 包含半巢狀 (semi-nested) 類別,及 "delete [] 陣列指標"。 
 * 3.0 包含全巢狀 (fully-nested) 類別、template 和 "i++" vs "++i"。 
 * 4.0 將包含例外處理。 
 
======================================== 
 
Q128:如果簽名編碼標準化了,我能否將不同廠商編譯器產生的程式碼連結起來? 
 
簡短的回答:可能不行。 
 
換句話說,有人希望標準化的簽名編碼規則能並入擬議中的 C++ ANSI 標準,避免還 
要為不同廠商的編譯器購買不同版本的物件程式庫。然而不同的系統實作中,簽名編 
碼的差異性只佔一小部份而已,即使是在同一個基台(platform)上。這裡列出一部 
份其他的差異處: 
 
1) 成員函數隱含的引數個數和型態。 
   1a) 'this' 有被特殊處理嗎? 
   1b) 傳值的指標放在哪裡? 
2) 假設有用到 vtable 虛擬表格的話: 
   2a) 它的內容及配置? 
   2b) 多重繼承時,'this' 在何處/如何調整? 
3) 類別如何配置,包含: 
   3a) 基底類別的位置? 
   3b) 虛擬基底類別的處理? 
   3c) 虛擬表格指標的位置,如果有用虛擬表格的話? 
4) 函數的呼叫慣例,包含: 
   4a) 呼叫者還是被呼叫者負責調整堆疊? 
   4b) 實際參數放到哪裡? 
   4c) 實際參數傳遞之順序? 
   4d) 暫存器如何存放? 
   4e) 傳回值放到哪裡? 
   4f) 對傳入/傳回 struct 或 double 有無特殊的規定? 
   4g) 呼叫末端函數(leaf function)有無特殊的暫存器存放規定? 
5) run-time-type-identification 如何配置? 
6) 當一個例外被 throw 時,執行期的例外處理系統如何得知哪一個區域物件該被解 
   構? 
 
 
======================================= 
■□ 第22節:其他的技術和環境的事項 
======================================= 
● 22A:其他的技術事項 
======================== 
 
Q129:為什麼有 static 資料成員的物件類別產生了 linker 錯誤? 
 
Static 的資料成員必須外顯地在唯一的模組中定義。 
                     ^^^^^^  ~~~~~~^^^^  ^^^^ 
【譯注】這句話要逐字細讀。原文是:Static data members must be 
        explicitly defined in exactly one module. 
 
譬如: 
        class Fred { 
        public: 
          //... 
        private: 
          static int i_;  // 宣告 static 資料成員 "Fred::i_" 
          //... 
        }; 
 
Linker 會告訴你 "Fred::i_ is not defined(未定義)" ,除非你在任何一個(且 
唯一)原始檔中定義(而非宣告)了 "Fred::i_" : 
 
        int Fred::i_ = 某個會產生 int 的運算式; 
或是: 
        int Fred::i_; 
 
通常我們會在 "Fred.C" 檔中定義 "Fred" 類別的 static 資料成員(或 "Fred.cpp" 
等等你使用的副檔名)。 
 
======================================== 
 
Q130:"struct" 和 "class" 關鍵字差別在哪? 
 
struct 的成員和基底類別, 都是預設為 public 的,而 class 則預設為 private。 
注意:你應該“明顯地”把基底類別設為 public、private 或是 protected,而不 
要依賴預設值。 
 
除此之外,兩者的功能是相等的。 
 
======================================== 
 
Q131:為什麼不能以函數的傳回值來多載(overload)它? 
 
如果你同時宣告了 "char f()" 及 "float f()" ,編譯器會給你個錯誤訊息,因為 
呼叫 "f()" 會造成模擬兩可的情況。 
 
======================================== 
 
Q132:什麼是「持續性」?什麼是「持續性物件」? 
 
一個持續性物件 (persistent object),在創造它的程式執行結束後,仍可存活下來 
。它甚至可存活於不同的父程式,存活於磁碟系統、作業系統、甚至於作業系統所處 
的硬體上。 
 
持續性物件的困難在於:如何有效地在次儲存體中,存放它們的運作行為(method) 
及資料位元(以及所有成員物件的資料和運作行為,及它們所有的成員物件、基底類 
別……等等)。這一切都得自己來做的話,可不是件容易的事。在 C++中,你就得自 
己來。C++/OO 的資料庫系統,會替你把這些機制都隱藏起來。 
 
======================================== 
 
Q133:為什麼浮點數 (floating point) 這麼不精確?為什麼這段程式不會印出 0.43? 
 
        #include 
 
        main() 
        { 
          float a = 1000.43; 
          float a = 1000.0; 
          cout << a - b << '\n'; 
        } 
 
(附注,有些 C++ 環境下會印出 0.429993) 
 
聲明:受進位/舍位/近似值之苦,其實並不是 C++ 的問題,而是電腦科學界的問 
題。不過還是一直有人在 comp.lang.c++ 裡發問,所以我給你一個答案意思一下。 
 
答案:浮點數本來就是個近似值。在 IEEE 的 32 位元浮點數標準裡,有 1 位元的 
正負號,8 位元的指數,23 位元的假數。因為正規化後的二進位假數都會變成像是 
1.xxxxx... 的型式,所以頭一項的 1 不予計入,就能得到 24 位元的有效假數。 
1000.43(以及其他很多很多數字)都不是 float 或 double 的表示法,其實 
1000.43 的位元內容是這樣子的('s' 代表正負號,'e' 代表指數,'m' 代表假數) 
: 
 
    seeeeeeeemmmmmmmmmmmmmmmmmmmmmmm 
    01000100011110100001101110000101 
 
假數移位後變成 1111101000.01101110000101 或是 1000 + 7045/16384。 
分數部份為 0.429992675781。 
float 的假數佔 24 位元,所以你只得到 16M 分之一的精確度。 
double 有較高的精確度(53 位元的假數)。 
 
========================== 
● 22B:其他環境下的瑣事 
========================== 
 
Q134:有任何 TeX 或 LaTeX 的巨集,能處理 "C++" 的留白效果(spacing)嗎? 
 
有的,底下列出兩個: 
 
\def\CC{C\raise.22ex\hbox{{\footnotesize +}}\raise.22ex\hbox{\footnotesize +}} 
 
\def\CC{{C\hspace{-.05em}\raisebox{.4ex}{\tiny\bf ++}}} 
 
======================================== 
 
Q135:在哪兒可拿到 C++2LaTeX 這個 C++原始碼的 LaTeX 美編工具(pretty 
      printer)? 
 
這兒列出一些 ftp 地點: 
 
Host aix370.rrz.uni-koeln.de   (134.95.80.1) Last updated 15:41 26 Apr 1991 
    Location: /tex 
      FILE      rw-rw-r--     59855  May  5  1990   C++2LaTeX-1.1.tar.Z 
Host utsun.s.u-tokyo.ac.jp   (133.11.11.11) Last updated 05:06 20 Apr 1991 
    Location: /TeX/macros 
      FILE      rw-r--r--     59855  Mar  4 08:16   C++2LaTeX-1.1.tar.Z 
Host nuri.inria.fr   (128.93.1.26) Last updated 05:23  9 Apr 1991 
    Location: /TeX/tools 
      FILE      rw-rw-r--     59855  Oct 23 16:05   C++2LaTeX-1.1.tar.Z 
Host iamsun.unibe.ch   (130.92.64.10) Last updated 05:06  4 Apr 1991 
    Location: /TeX 
      FILE      rw-r--r--     59855  Apr 25  1990   C++2LaTeX-1.1.tar.Z 
Host iamsun.unibe.ch   (130.92.64.10) Last updated 05:06  4 Apr 1991 
    Location: /TeX 
      FILE      rw-r--r--     51737  Apr 30  1990 
      C++2LaTeX-1.1-PL1.tar.Z 
Host tupac-amaru.informatik.rwth-aachen.de   (192.35.229.9) 
Last updated 05:07 18 Apr 1991 
    Location: /pub/textproc/TeX 
      FILE      rw-r--r--     72957  Oct 25 13:51  C++2LaTeX-1.1-PL4.tar.Z 
Host wuarchive.wustl.edu   (128.252.135.4) Last updated 23:25 30 Apr 1991 
    Location: /packages/tex/tex/192.35.229.9/textproc/TeX 
      FILE      rw-rw-r--     49104  Apr 10  1990   C++2LaTeX-PL2.tar.Z 
      FILE      rw-rw-r--     25835  Apr 10  1990   C++2LaTeX.tar.Z 
Host tupac-amaru.informatik.rwth-aachen.de   (192.35.229.9) 
Last updated 05:07 18 Apr 1991 
    Location: /pub/textproc/TeX 
      FILE rw-r--r-- 74015  Mar 22 16:23 C++2LaTeX-1.1-PL5.tar.Z 
    Location: /pub 
      FILE rw-r--r-- 74015  Mar 22 16:23 C++2LaTeX-1.1-PL5.tar.Z 
Host sol.cs.ruu.nl   (131.211.80.5) Last updated 05:10 15 Apr 1991 
    Location: /TEX/TOOLS 
      FILE      rw-r--r--     74015  Apr  4 21:02x   C++2LaTeX-1.1-PL5.tar.Z 
Host tupac-amaru.informatik.rwth-aachen.de (192.35.229.9) 
Last updated 05:07 18 Apr 1991 
    Location: /pub/textproc/TeX 
      FILE      rw-r--r--      4792  Sep 11  1990 C++2LaTeX-1.1-patch#1 
      FILE      rw-r--r--      2385  Sep 11  1990 C++2LaTeX-1.1-patch#2 
      FILE      rw-r--r--      5069  Sep 11  1990 C++2LaTeX-1.1-patch#3 
      FILE      rw-r--r--      1587  Oct 25 13:58 C++2LaTeX-1.1-patch#4 
      FILE      rw-r--r--      8869  Mar 22 16:23 C++2LaTeX-1.1-patch#5 
      FILE      rw-r--r--      1869  Mar 22 16:23 C++2LaTeX.README 
Host rusmv1.rus.uni-stuttgart.de   (129.69.1.12) 
Last updated 05:13 13 Apr 1991 
    Location: /soft/tex/utilities 
      FILE      rw-rw-r--    163840  Jul 16  1990   C++2LaTeX-1.1.tar 
 
======================================== 
 
Q136:該到哪裡取得 "tgrind" 這個 C++/C/etc 的原始碼美編工具? 
 
"tgrind" 讀入 C++ 原始檔案,並輸出能讓 Unix 印表機印出美觀文件的東西。它常 
會伴隨在 TeX 和 LaTeX 的套件裡;請找找這個目錄: 
 "...tex82/contrib/van/tgrind" 。 由 Jerry Leichter 所做更新的版本,可在 
venus.ycc.yale.edu in [.TGRIND] 裡找到。 
 
======================================== 
 
Q137:有給 GNU emacs 編輯器用的 C++-mode 嗎?有的話,該怎麼拿? 
 
Yes,有一個給 GNU emacs 用的 C++-mode。 
 
最新、最好的 C++-mode(以及 c-mode)版本是 cc-mode.el 檔,是 Detlef & 
Clamen 版本的延伸。Emacs 裡頭有一個了,較新的則在 elisp 裡面。 
 
======================================== 
 
Q138:我要到哪兒得到和作業系統相關的 FAQs( 譬如:BC++、DOS、Windows 等等 
      )? 
 
請參考: 
 * comp.os.msdos.programmer 
 * comp.windows.ms.programmer 
 * comp.unix.programmer 
 
[如果您有 BC++、VC++ 的 email address,或是 Semantic C++ 的臭□清單或可供 
討論的 mailing list,請告訴我該如何加入,我會在這兒提出的。] 
 
======================================== 
 
Q139:為什麼我的 DOS C++ 程式說 "Sorry: floating point code not linked" 
      “抱歉,浮點運算程式碼未連結進來”? 
 
編譯器會試節省執行檔的大小,所以除非必要,否則不引入浮點數→字串格式轉換 
的副程式,可是有時候它會猜錯,就會產生上述的錯誤訊息了。解決法:(1) 使用 
 而不要用 ,或是 (2) 在您程式的某個地方,置入如下的函 
數(但是不要真的去呼叫它!): 
 
        static void dummyfloat(float *x) { float y; dummyfloat(&y); } 
 
請參考關於 stream I/O 的 FAQ項目,有提到更多使用  vs  
的理由。 
 
======================================== 
 
Q140:為什麼當我沒執行 BC45 IDE 的話,BC++ 做出來的 Windows 應用程式就不能 
      用? 
 
用 BC++ 寫 Windows 應用程式,如果當 BC45 IDE 正在執行時,你的程式很正常; 
待會兒當 BC45 IDE 關掉了,而你的程式卻在建立視窗時產生了個 exception 的話 
,就把底下這行程式加到你的應用程式類別 ("YourApp::InitMainWindow()") 裡頭 
的 InitMainWindow() 內: 
 
        EnableBWCC(TRUE); 
 
【譯注】這是因為你用 BC++ 寫的應用程式,可能會自動用到 bwcc*.dll,剛好 
        BC++ 的 IDE 也會用到它,所以兩者並存的話,BWCC 已先被 IDE 載入了。 
        若是 IDE 未執行,則 BWCC 未被載入,你就得用上面那一行程式來通知 
        OWL 去載入它。 
 
== comp.lang.c++ FAQ 結束 ========================= 
(http://www.fanqiang.com)
    進入【UNIX論壇

相關文章
C++語言常見問題解答(4) (2001-04-27 13:08:06)
C++語言常見問題解答(3) (2001-04-27 13:07:23)
C++語言常見問題解答(2) (2001-04-27 13:06:27)
C++語言常見問題解答(1) (2001-04-27 13:05:08)
 

★  樊強制作 歡迎分享  ★