• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            chaosuper85

            C++博客 首頁 新隨筆 聯(lián)系 聚合 管理
              118 Posts :: 0 Stories :: 3 Comments :: 0 Trackbacks

            #

            開發(fā)輔助工具大收集

            除了我們?nèi)粘i_發(fā)使用的Visual C++、Delphi、JBuilder等等大家伙,
            還有很多小巧好用的開發(fā)輔助工具,善用它們可以極大的提高我們的效率。

            日常工作中我主要使用Visual C++開發(fā)程序,工作幾年,收集了一些小工具,
            下面逐個(gè)介紹給大家。也希望大家把自己的工具拿出來和大家分享。

            如果大家需要,請(qǐng)跟貼,需要的朋友多的話,我上傳到FTP上(都是沒有限制的最新版本喲)

            Visual C++插件

            [1] Visual Assist

            http://www.wholetomato.com/download/index.shtml

            這是我現(xiàn)在使用最為頻繁的工具,有了它,現(xiàn)在在Visual C++中寫程序簡(jiǎn)直成了一種享受,
            Visual Assist的智能提示功能實(shí)在是太強(qiáng)大了,估計(jì)大家都應(yīng)該裝了吧!
            唯一不太爽的是Visual Assist對(duì)C++ STL和Templates的解析還是有待改進(jìn)。

            [2] WndTab

            http://www.wndtabs.com

            Visual C++ 6.0的編輯窗口沒有分頁顯示,
            想在打開的多個(gè)文件中切換非常麻煩,
            WndTab為VC的編輯窗口加上了Tab,
            現(xiàn)在點(diǎn)擊每個(gè)文件的Tab就可以方便的切換到該文件進(jìn)行編輯了,強(qiáng)烈推薦。

            [3] BoundsCheck

            CompuWare的調(diào)試工具,可以集成到Visual C++中。
            BoundsCheck可以幫助我們發(fā)現(xiàn)程序中隱藏的bug,比如Memory Leak等。
            缺省安裝后,BoundsCheck的設(shè)置是每當(dāng)發(fā)現(xiàn)調(diào)試狀態(tài)下運(yùn)行的程序中的bug就馬上中斷執(zhí)行,返回Visual C++窗口報(bào)告bug,但是很多BoundsCheck發(fā)現(xiàn)的bug都是一些程序隱患,但不影響當(dāng)前程序運(yùn)行,所以有些討厭。可以在BoundsCheck的工具欄中將立即報(bào)告錯(cuò)誤按鈕釋放,以后我們就可以不被BoundsCheck打擾,而是每次調(diào)試后得到一份BoundsCheck的bug匯總報(bào)告!

            其他工具

            [4] 界面庫Xtreme Toolkit

            http://www.codejock.com

            和Xtreme Toolkit類似的還有BCG Controls,但是我覺得Xtreme Toolkit更好用一些,它們都提供了一整套功能強(qiáng)大、非常漂亮的控件,幫助我們輕松創(chuàng)建出很Cool的程序界面,從而把主要精力放到程序功能上。

            [5] IconXP

            http://www.aha-soft.com

            制作程序的各種圖標(biāo),如果利用Visual C++或者Delphi等自帶的資源編輯器,只能編輯256色的圖標(biāo),非常麻煩而且基本無法編輯出XP風(fēng)格的圖標(biāo)來。利用IconXP可以輕松創(chuàng)作出很Cool的圖標(biāo)來,而且IconXP能夠從各種文件中提取出圖標(biāo)文件。

            寫了這么多,累了,明天繼續(xù)……
            [6] OllyDbg

            http://home.t-online.de/home/Ollydbg/

            這是一個(gè)很Cool的靜態(tài)反匯編工具,并且能夠在反匯編代碼的基礎(chǔ)上對(duì)應(yīng)用程序進(jìn)行調(diào)試。
            個(gè)人認(rèn)為OllyDbg比很多crack網(wǎng)站上推薦的WDASM好用,因?yàn)镺llyDbg加入了很多對(duì)反匯編代碼的進(jìn)一步分析功能,并加上相應(yīng)的注釋,非常方便。
            比如應(yīng)用程序在某處調(diào)用了Windows API函數(shù),該處后面就會(huì)出現(xiàn)注釋告訴你這里調(diào)用了哪個(gè)Windows API函數(shù),更酷的是連給該Windows API傳遞參數(shù)的地方也會(huì)加上注釋說明。
            另外由于很多應(yīng)用程序都是使用Visual C++編寫,而Visual C++生成的匯編代碼有一定的格式(如果沒有選擇某些優(yōu)化功能的時(shí)候),所以O(shè)llyDbg甚至?xí)⒁恍﹨R編代碼對(duì)應(yīng)的C語言代碼以注釋的方式說明。

            OllyDbg本身的調(diào)試功能也很強(qiáng)大,多用幾次就會(huì)得心應(yīng)手。

            總而言之,OllyDbg絕對(duì)是在沒有源代碼的情況下分析應(yīng)用程序的必備工具。

            CodeProject上有兩篇文章FreeCell & Hearts, behind the scenes和Minesweeper, Behind the scenes,作者就是以O(shè)llyDbg為工具探索到了Windows附帶的掃雷游戲、空當(dāng)接龍游戲的底層數(shù)據(jù)結(jié)構(gòu),從而寫出了直接讀取這些游戲內(nèi)存的程序,我稍加修改就做了一個(gè)自動(dòng)掃雷的程序,呵呵。

            以下程序在http://www.sysinternals.com有提供

            [7] DebugView

            看過《深入淺出MFC》嗎,候捷先生在書的最后提到了一種追蹤TRACE(實(shí)際上是Windows函數(shù)OutputDebugString)的工具。有了該工具,你就可以在應(yīng)用程序運(yùn)行時(shí)通過它觀察追蹤應(yīng)用程序內(nèi)部的運(yùn)行情況,只要你在程序中加了足夠多的TRACE宏,并且以Debug版本編譯。
            特別是對(duì)于程序邏輯復(fù)雜(Debug幾次就暈了),或者涉及到圖形界面刷新或顯示的程序(如果用一臺(tái)電腦調(diào)試,在Visual C++環(huán)境和被調(diào)試程序之間切換,你很難看到正確的結(jié)果),或者非常耗費(fèi)系統(tǒng)資源的程序(在用Visual C++調(diào)試運(yùn)行,就更費(fèi)勁了),巧妙的使用這類工具可以高效的解決問題。
            說實(shí)話,Paul DiLascia等大師固然提供了這些工具,但是這些大師只是為了展示某些技術(shù),所以他們提供的工具都只有基本功能。而DebugView是同類工具中最為優(yōu)秀的一個(gè),適用范圍廣,能夠定制各種過濾條件,讓你只看到關(guān)心的TRACE輸出信息,而且可以定制高亮顯示的內(nèi)容等等,非常方便。
            DebugView是完全免費(fèi)的!

            [8]
            Disk Monitor
            File Monitor
            Register Monitor
            Port Monitor


            這系列Monitor工具分別對(duì)系統(tǒng)中的磁盤、文件、注冊(cè)表、端口的變化更改進(jìn)行實(shí)時(shí)監(jiān)控并記錄下來,對(duì)于我們追蹤程序?qū)ο到y(tǒng)進(jìn)行了那些更改特別有用。

            SysInternals上面還有很多工具,都是免費(fèi)的,有些還提供源代碼。

            上面是我經(jīng)常使用的開發(fā)輔助工具,有些可能一時(shí)沒有想到,待以后慢慢在這里補(bǔ)全。
            因我主要使用Visual C++進(jìn)行開發(fā),所以介紹的工具也都主要是和Visual C++相關(guān)的,希望有朋友能夠?qū)⑵渌饕_發(fā)工具的好的配套輔助工具也來個(gè)介紹。

            另外,如果有朋友需要上面介紹的工具,請(qǐng)跟貼,我試情況上傳到FTP上供大家下載。

            posted @ 2009-08-19 22:29 chaosuper 閱讀(277) | 評(píng)論 (0)編輯 收藏

            我們很難寫出所有可能被實(shí)例化的類型都合適的模板。某些情況下,

                通用模板定義對(duì)于某個(gè)類型可能是完全錯(cuò)誤的,所以我們需要能夠?qū)崿F(xiàn)處

                理某些特殊情況,特化的概念變是如此。compare函數(shù)和Queue類是這

                個(gè)問題的很好例子。因?yàn)榕cC風(fēng)格字符串一起使用時(shí),他們都不能正確工作

                template <typename T>
                int compare(const T &v1,const T &v2)
                {
                  if(v1 < v2) return -1;
                  if(v2 < v1) return 1;
                  return 0;
                }

                    如果用兩個(gè)const char* 實(shí)參調(diào)用這個(gè)模板定義,函數(shù)將比較指針的
                值。也就是比較兩個(gè)指針在內(nèi)存中的相對(duì)位置,卻并沒有說明與指針?biāo)?br>    數(shù)組的內(nèi)容有關(guān)的任何事情。
                    為了能夠?qū)ompare函數(shù)用于字符串,必須提供一個(gè)知道怎樣比較C風(fēng)
                格字符串的特殊定義。這些就被稱作是特化的,它對(duì)模板的用戶而言是透
                明的。


                1. 函數(shù)模板的特化
                特化形式:
                - 關(guān)鍵字template后面接一對(duì)空的尖括號(hào)<>;
                - 再接模板名和一對(duì)尖括號(hào)<>,尖括號(hào)中指定這個(gè)特化定義的模板參數(shù):
                - 函數(shù)形參表
                - 函數(shù)體

                template<>
                int compare<const char*> (const char* const &v1,
                     const char* const &v2)
                {
                  return strcmp(v1,v2);
                }
                    特化的聲明必須與 對(duì)應(yīng)的模板相匹配。類型形參固定為const char*。
                因此,函數(shù)形參是const char* 的const引用。當(dāng)調(diào)用compare函數(shù)的時(shí)候,
                傳給它兩個(gè)字符指針,編譯器將調(diào)用特化版本。而不調(diào)用上面的泛型版本。
                  const  char *cp1 = "world", *cp2 = "hi";
                  int i1, i2;
                  compare(cp1, cp2); //調(diào)用特化函數(shù)模板
                  compare(i1, i2);   //調(diào)用泛型函數(shù)模板

                注意:
                * 函數(shù)模板特化時(shí)template<>不能省略,如果缺少結(jié)果是聲明該函數(shù)的重載


                * 必須包含函數(shù)形參列表。如果可以從形參列表推斷模板實(shí)參,則不必顯示

                   定模板實(shí)參。
                * 如果程序由多個(gè)文件構(gòu)成,模板特化的聲明必須在使用該特化的每個(gè)文件
                   中出現(xiàn)。


                2.類模板的特化
                  當(dāng)使用C風(fēng)格字符串時(shí),Queue類具有 compare函數(shù)相似的問題。問題就處
                在push函數(shù)中,該函數(shù)復(fù)制給定的值以創(chuàng)建Queue中的新元素。默認(rèn)情況下


                復(fù)制C風(fēng)格字符串只會(huì)復(fù)制指針,不會(huì)復(fù)制字符。而顯然復(fù)制指針將出現(xiàn)一


                列的嚴(yán)重問題。為了解決復(fù)制C風(fēng)格字符串的問題,需要為const char*定義
                整個(gè)類的特化版本:
                  template<> class Queue<const char*>{
                  public:
                    void push(const char*);
                    void pop() {real_queue.pop();}
                    bool empty() const {return real_queue.front();}
                    //返回類型與模板參數(shù)類型不同
                    std::string front() {return real_queue.front();}
                    const std::string &front() const {return real_queue.front();

                  private :
                    Queue<std::string> real_queue;
                };
                給Queue一個(gè)新的數(shù)據(jù)元素,string對(duì)象的Queue。
                在類的外部定義一個(gè)成員:
                  void Queue<const char*>::push (const char* val)
                  {
                    return real_queue.push(val);
                  }
                這個(gè)函數(shù)通過調(diào)用read_queue的push函數(shù)把val指向的數(shù)組復(fù)制到未命名的
                string 對(duì)象中。當(dāng)需要出隊(duì)列的時(shí)候調(diào)用相應(yīng)real_queue.pop()函數(shù)即返
                回了這個(gè)string,從而解決了不用復(fù)制指針的問題。

               3.特化成員而不特化類
                在上例的實(shí)現(xiàn)中,我們可以換一種方法,即不需要特化類,而只需要特化類
                的成員函數(shù)push、pop。

                根據(jù)函數(shù)模板特化的要求:
                template <>
                void Queue<const char*>::push(const char *const &val)
                {
                  char * new_item = new char[strlen(val)+1];
                  strncpy(new_item, val, strlen(val)+1);
                  QueueItem<const char*> *pt =
                 new QueueItem<const char*>(new_item);
                  if(empty())
                    head = tail = pt;   //隊(duì)列中沒有元素
                  eles{
                    tail->next = pt; //添加新元素到列尾
                    tail = pt;
                  }
                }

                template<>
                void Queue<const char*>::pop()
                {
                  QueueItem<const char*> *p = head;
                  delete head->item;  //刪除隊(duì)首元素
                  head = head->next;  //指向當(dāng)前隊(duì)首元素
                  delete p;           //刪除零時(shí)指針
                }


                4.類模板的部分特化
                如果類模板有一個(gè)以上的模板形參,我們很有可能只要特化某些模板形參
                而不是全部形參。這時(shí)我們就需要使用類的部分特化。

                //定義模板類
                template <class T1, class T2>
                class some_template{
                  // ...
                };

                //定義模板類的部分特化:T2類型固定,部分特化T1類型
                template<class T1>
                class some_template<T1, int>{
                  // ...
                };

                //使用類模板的部分特化
                some_template<int, string> foo; //使用模板類
                some_template<string,int> bar;  //使用模板類的部分特化

                通過使用模板特化能解決一些在通常或者通用情況下無法解決的特殊問題。

                在掌握了基本的語法規(guī)范和實(shí)現(xiàn)方法后便可以加以應(yīng)用。

             

            posted @ 2009-08-11 18:58 chaosuper 閱讀(546) | 評(píng)論 (0)編輯 收藏

            Q:什么是C風(fēng)格轉(zhuǎn)換?什么是static_cast, dynamic_cast 以及

            reinterpret_cast?區(qū)別是什么?為什么要注意?

            A:轉(zhuǎn)換的含義是通過改變一個(gè)變量的類型為別的類型從而改變?cè)撟兞康谋硎痉绞?/p>

            。為了類型轉(zhuǎn)換一個(gè)簡(jiǎn)單對(duì)象為另一個(gè)對(duì)象你會(huì)使用傳統(tǒng)的類型轉(zhuǎn)換操作符。比

            如,為了轉(zhuǎn)換一個(gè)類型為doubole的浮點(diǎn)數(shù)的指針到整型:
            代碼:
            int i;
            double d;

            i = (int) d;
            或者:

            i = int (d);

            對(duì)于具有標(biāo)準(zhǔn)定義轉(zhuǎn)換的簡(jiǎn)單類型而言工作的很好。然而,這樣的轉(zhuǎn)換符也能不

            分皂白的應(yīng)用于類(class)和類的指針。ANSI-C++標(biāo)準(zhǔn)定義了四個(gè)新的轉(zhuǎn)換符:

            'reinterpret_cast', 'static_cast', 'dynamic_cast' 和 'const_cast',目的

            在于控制類(class)之間的類型轉(zhuǎn)換。
            代碼:
            reinterpret_cast<new_type>(expression)
            dynamic_cast<new_type>(expression)
            static_cast<new_type>(expression)
            const_cast<new_type>(expression)


            1 reinterpret_cast

            'reinterpret_cast'轉(zhuǎn)換一個(gè)指針為其它類型的指針。它也允許從一個(gè)指針轉(zhuǎn)換

            為整數(shù)類型。反之亦然。(譯注:是指針具體的地址值作為整數(shù)值?)
            這個(gè)操作符能夠在非相關(guān)的類型之間轉(zhuǎn)換。操作結(jié)果只是簡(jiǎn)單的從一個(gè)指針到別

            的指針的值的二進(jìn)制拷貝。在類型之間指向的內(nèi)容不做任何類型的檢查和轉(zhuǎn)換。

            如果情況是從一個(gè)指針到整型的拷貝,內(nèi)容的解釋是系統(tǒng)相關(guān)的,所以任何的實(shí)

            現(xiàn)都不是方便的。一個(gè)轉(zhuǎn)換到足夠大的整型能夠包含它的指針是能夠轉(zhuǎn)換回有效

            的指針的。

            代碼:
            class A {};
            class B {};

            A * a = new A;
            B * b = reinterpret_cast<B *>(a);
            'reinterpret_cast'就像傳統(tǒng)的類型轉(zhuǎn)換一樣對(duì)待所有指針的類型轉(zhuǎn)換。

            2 static_cast

            'static_cast'允許執(zhí)行任意的隱式轉(zhuǎn)換和相反轉(zhuǎn)換動(dòng)作。(即使它是不允許隱式

            的)

            應(yīng)用到類的指針上,意思是說它允許子類類型的指針轉(zhuǎn)換為父類類型的指針(這

            是一個(gè)有效的隱式轉(zhuǎn)換),同時(shí),也能夠執(zhí)行相反動(dòng)作:轉(zhuǎn)換父類為它的子類。

            在這最后例子里,被轉(zhuǎn)換的父類沒有被檢查是否與目的類型相一致。
            代碼:
            class Base {};
            class Derived : public Base {};

            Base *a    = new Base;
            Derived *b = static_cast<Derived *>(a);
            'static_cast'除了操作類型指針,也能用于執(zhí)行類型定義的顯式的轉(zhuǎn)換,以及基

            礎(chǔ)類型之間的標(biāo)準(zhǔn)轉(zhuǎn)換:

            代碼:
            double d = 3.14159265;
            int    i = static_cast<int>(d);

            3 dynamic_cast

            'dynamic_cast'只用于對(duì)象的指針和引用。當(dāng)用于多態(tài)類型時(shí),它允許任意的隱

            式類型轉(zhuǎn)換以及相反過程。不過,與static_cast不同,在后一種情況里(注:即

            隱式轉(zhuǎn)換的相反過程),dynamic_cast會(huì)檢查操作是否有效。也就是說,它會(huì)檢

            查轉(zhuǎn)換是否會(huì)返回一個(gè)被請(qǐng)求的有效的完整對(duì)象。
            檢測(cè)在運(yùn)行時(shí)進(jìn)行。如果被轉(zhuǎn)換的指針不是一個(gè)被請(qǐng)求的有效完整的對(duì)象指針,

            返回值為NULL.
            代碼:
            class Base { virtual dummy() {} };
            class Derived : public Base {};

            Base* b1 = new Derived;
            Base* b2 = new Base;

            Derived* d1 = dynamic_cast<Derived *>(b1);          // succeeds
            Derived* d2 = dynamic_cast<Derived *>(b2);          // fails: returns

            'NULL'

            如果一個(gè)引用類型執(zhí)行了類型轉(zhuǎn)換并且這個(gè)轉(zhuǎn)換是不可能的,一個(gè)bad_cast的異

            常類型被拋出:
            代碼:
            class Base { virtual dummy() {} };
            class Derived : public Base { };

            Base* b1 = new Derived;
            Base* b2 = new Base;

            Derived d1 = dynamic_cast<Derived &*>(b1);          // succeeds
            Derived d2 = dynamic_cast<Derived &*>(b2);          // fails: exception

            thrown

            4 const_cast

            這個(gè)轉(zhuǎn)換類型操縱傳遞對(duì)象的const屬性,或者是設(shè)置或者是移除:
            代碼:
            class C {};

            const C *a = new C;

            C *b = const_cast<C *>(a);
            其它三種操作符是不能修改一個(gè)對(duì)象的常量性的。
            注意:'const_cast'也能改變一個(gè)類型的volatile qualifier。

            --------------------------------------------------------------------

            C++的4種類型轉(zhuǎn)換

                一、C 風(fēng)格(C-style)強(qiáng)制轉(zhuǎn)型如下:

                (T) expression // cast expression to be of type T
                函數(shù)風(fēng)格(Function-style)強(qiáng)制轉(zhuǎn)型使用這樣的語法:
                T(expression) // cast expression to be of type T
                這兩種形式之間沒有本質(zhì)上的不同,它純粹就是一個(gè)把括號(hào)放在哪的問題。

            我把這兩種形式稱為舊風(fēng)格(old-style)的強(qiáng)制轉(zhuǎn)型。

               二、 C++的四種強(qiáng)制轉(zhuǎn)型形式:

              C++ 同時(shí)提供了四種新的強(qiáng)制轉(zhuǎn)型形式(通常稱為新風(fēng)格的或 C++ 風(fēng)格的強(qiáng)

            制轉(zhuǎn)型):
              const_cast(expression)
              dynamic_cast(expression)
              reinterpret_cast(expression)
              static_cast(expression)

              每一種適用于特定的目的:

              ·dynamic_cast 主要用于執(zhí)行“安全的向下轉(zhuǎn)型(safe downcasting)”,

            也就是說,要確定一個(gè)對(duì)象是否是一個(gè)繼承體系中的一個(gè)特定類型。它是唯一不

            能用舊風(fēng)格語法執(zhí)行的強(qiáng)制轉(zhuǎn)型,也是唯一可能有重大運(yùn)行時(shí)代價(jià)的強(qiáng)制轉(zhuǎn)型。
               
                ·static_cast 可以被用于強(qiáng)制隱型轉(zhuǎn)換(例如,non-const 對(duì)象轉(zhuǎn)型為

            const 對(duì)象,int 轉(zhuǎn)型為 double,等等),它還可以用于很多這樣的轉(zhuǎn)換的反向

            轉(zhuǎn)換(例如,void* 指針轉(zhuǎn)型為有類型指針,基類指針轉(zhuǎn)型為派生類指針),但

            是它不能將一個(gè) const 對(duì)象轉(zhuǎn)型為 non-const 對(duì)象(只有 const_cast 能做到

            ),它最接近于C-style的轉(zhuǎn)換。
               
              ·const_cast 一般用于強(qiáng)制消除對(duì)象的常量性。它是唯一能做到這一點(diǎn)的

            C++ 風(fēng)格的強(qiáng)制轉(zhuǎn)型。

              ·reinterpret_cast 是特意用于底層的強(qiáng)制轉(zhuǎn)型,導(dǎo)致實(shí)現(xiàn)依賴

            (implementation-dependent)(就是說,不可移植)的結(jié)果,例如,將一個(gè)指

            針轉(zhuǎn)型為一個(gè)整數(shù)。這樣的強(qiáng)制轉(zhuǎn)型在底層代碼以外應(yīng)該極為罕見。
              
              舊風(fēng)格的強(qiáng)制轉(zhuǎn)型依然合法,但是新的形式更可取。首先,在代碼中它們更

            容易識(shí)別(無論是人還是像 grep 這樣的工具都是如此),這樣就簡(jiǎn)化了在代碼

            中尋找類型系統(tǒng)被破壞的地方的過程。第二,更精確地指定每一個(gè)強(qiáng)制轉(zhuǎn)型的目

            的,使得編譯器診斷使用錯(cuò)誤成為可能。例如,如果你試圖使用一個(gè) const_cast

            以外的新風(fēng)格強(qiáng)制轉(zhuǎn)型來消除常量性,你的代碼將無法編譯。

            == 
            ==  dynamic_cast .vs. static_cast
            ==

            class B { ... };
            class D : public B { ... };

            void f(B* pb)
            {
               D* pd1 = dynamic_cast<D*>(pb);
               D* pd2 = static_cast<D*>(pb);
            }

            If pb really points to an object of type D, then pd1 and pd2 will get

            the same value. They will also get the same value if pb == 0.

            If pb points to an object of type B and not to the complete D class,

            then dynamic_cast will know enough to return zero. However, static_cast

            relies on the programmer’s assertion that pb points to an object of

            type D and simply returns a pointer to that supposed D object.

                即dynamic_cast可用于繼承體系中的向下轉(zhuǎn)型,即將基類指針轉(zhuǎn)換為派生類

            指針,比static_cast更嚴(yán)格更安全。dynamic_cast在執(zhí)行效率上比static_cast

            要差一些,但static_cast在更寬上范圍內(nèi)可以完成映射,這種不加限制的映射伴隨

            著不安全性.static_cast覆蓋的變換類型除類層次的靜態(tài)導(dǎo)航以外,還包括無映射

            變換,窄化變換(這種變換會(huì)導(dǎo)致對(duì)象切片,丟失信息),用VOID*的強(qiáng)制變換,隱式類

            型變換等...


            ==
            ==  static_cast .vs. reinterpret_cast
            ==

                reinterpret_cast是為了映射到一個(gè)完全不同類型的意思,這個(gè)關(guān)鍵詞在我們

            需要把類型映射回原有類型時(shí)用到它.我們映射到的類型僅僅是為了故弄玄虛和其

            他目的,這是所有映射中最危險(xiǎn)的.(這句話是C++編程思想中的原話)

                static_cast 和 reinterpret_cast 操作符修改了操作數(shù)類型. 它們不是互

            逆的; static_cast 在編譯時(shí)使用類型信息執(zhí)行轉(zhuǎn)換, 在轉(zhuǎn)換執(zhí)行必要的檢測(cè)(諸

            如指針越界計(jì)算, 類型檢查). 其操作數(shù)相對(duì)是安全的. 另一方面,

            reinterpret_cast 僅僅是重新解釋了給出的對(duì)象的比特模型而沒有進(jìn)行二進(jìn)制轉(zhuǎn)

            換, 例子如下:

                int n=9; double d=static_cast < double > (n);

                上面的例子中, 我們將一個(gè)變量從 int 轉(zhuǎn)換到 double. 這些類型的二進(jìn)制

            表達(dá)式是不同的. 要將整數(shù) 9 轉(zhuǎn)換到 雙精度整數(shù) 9, static_cast 需要正確地

            為雙精度整數(shù) d 補(bǔ)足比特位. 其結(jié)果為 9.0. 而reinterpret_cast 的行為卻不

            同:

                int n=9;
                double d=reinterpret_cast<double & > (n);

                這次, 結(jié)果有所不同. 在進(jìn)行計(jì)算以后, d 包含無用值. 這是因?yàn)?

            reinterpret_cast 僅僅是復(fù)制 n 的比特位到 d, 沒有進(jìn)行必要的分析.

            posted @ 2009-08-07 19:13 chaosuper 閱讀(2544) | 評(píng)論 (0)編輯 收藏

            Linux環(huán)境下的軟件安裝,并不是一件容易的事情;如果通過源代碼編譯后在安裝,當(dāng)然事情就更為復(fù)雜一些;現(xiàn)在安裝各種軟件的教程都非常普遍;但萬變不離其中,對(duì)基礎(chǔ)知識(shí)的扎實(shí)掌握,安裝各種軟件的問題就迎刃而解了。Configure腳本配置工具就是基礎(chǔ)之一,它是autoconf的工具的基本應(yīng)用。
                  
                與一些技巧相比,Configure顯得基礎(chǔ)一些,當(dāng)然使用和學(xué)習(xí)起來就顯得枯燥乏味一些,當(dāng)然要成為高手,對(duì)基礎(chǔ)的熟悉不能超越哦。
                  
                為此我轉(zhuǎn)載了一篇關(guān)于Configure選項(xiàng)配置的詳細(xì)介紹。供大家參考

                'configure'腳本有大量的命令行選項(xiàng).對(duì)不同的軟件包來說,這些選項(xiàng)可能會(huì)有變化,但是許多基本的選項(xiàng)是不會(huì)改變的.帶上'--help'選項(xiàng)執(zhí)行'configure'腳本可以看到可用的所有選項(xiàng).盡管許多選項(xiàng)是很少用到的,但是當(dāng)你為了特殊的需求而configure一個(gè)包時(shí),知道他們的存在是很有益處的.下面對(duì)每一個(gè)選項(xiàng)進(jìn)行簡(jiǎn)略的介紹:

                --cache-file=FILE
                  'configure'會(huì)在你的系統(tǒng)上測(cè)試存在的特性(或者bug!).為了加速隨后進(jìn)行的配置,測(cè)試的結(jié)果會(huì)存儲(chǔ)在一個(gè)cache file里.當(dāng)configure一個(gè)每個(gè)子樹里都有'configure'腳本的復(fù)雜的源碼樹時(shí),一個(gè)很好的cache file的存在會(huì)有很大幫助.

                --help
                  輸出幫助信息.即使是有經(jīng)驗(yàn)的用戶也偶爾需要使用使用'--help'選項(xiàng),因?yàn)橐粋€(gè)復(fù)雜的項(xiàng)目會(huì)包含附加的選項(xiàng).例如,GCC包里的'configure'腳本就包含了允許你控制是否生成和在GCC中使用GNU匯編器的選項(xiàng).

                --no-create
                  'configure'中的一個(gè)主要函數(shù)會(huì)制作輸出文件.此選項(xiàng)阻止'configure'生成這個(gè)文件.你可以認(rèn)為這是一種演習(xí)(dry run),盡管緩存(cache)仍然被改寫了.

                --quiet
                --silent
                  當(dāng)'configure'進(jìn)行他的測(cè)試時(shí),會(huì)輸出簡(jiǎn)要的信息來告訴用戶正在作什么.這樣作是因?yàn)?configure'可能會(huì)比較慢,沒有這種輸出的話用戶將會(huì)被扔在一旁疑惑正在發(fā)生什么.使用這兩個(gè)選項(xiàng)中的任何一個(gè)都會(huì)把你扔到一旁.(譯注:這兩句話比較有意思,原文是這樣的:If there was no such output, the user would be left wondering what is happening. By using this option, you too can be left wondering!)

                --version
                  打印用來產(chǎn)生'configure'腳本的Autoconf的版本號(hào).

                --prefix=PEWFIX
                  '--prefix'是最常用的選項(xiàng).制作出的'Makefile'會(huì)查看隨此選項(xiàng)傳遞的參數(shù),當(dāng)一個(gè)包在安裝時(shí)可以徹底的重新安置他的結(jié)構(gòu)獨(dú)立部分. 舉一個(gè)例子,當(dāng)安裝一個(gè)包,例如說Emacs,下面的命令將會(huì)使Emacs Lisp file被安裝到"/opt/gnu/share":
                $ ./configure --prefix=/opt/gnu

                --exec-prefix=EPREFIX
                  與'--prefix'選項(xiàng)類似,但是他是用來設(shè)置結(jié)構(gòu)倚賴的文件的安裝位置.編譯好的'emacs'二進(jìn)制文件就是這樣一個(gè)問件.如果沒有設(shè)置這個(gè)選項(xiàng)的話,默認(rèn)使用的選項(xiàng)值將被設(shè)為和'--prefix'選項(xiàng)值一樣.

                --bindir=DIR
                  指定二進(jìn)制文件的安裝位置.這里的二進(jìn)制文件定義為可以被用戶直接執(zhí)行的程序.

                --sbindir=DIR
                  指定超級(jí)二進(jìn)制文件的安裝位置.這是一些通常只能由超級(jí)用戶執(zhí)行的程序.

                --libexecdir=DIR
                  指定可執(zhí)行支持文件的安裝位置.與二進(jìn)制文件相反,這些文件從來不直接由用戶執(zhí)行,但是可以被上面提到的二進(jìn)制文件所執(zhí)行.

                --datadir=DIR
                  指定通用數(shù)據(jù)文件的安裝位置.

                --sysconfdir=DIR
                  指定在單個(gè)機(jī)器上使用的只讀數(shù)據(jù)的安裝位置.

                --sharedstatedir=DIR
                  指定可以在多個(gè)機(jī)器上共享的可寫數(shù)據(jù)的安裝位置.

                --localstatedir=DIR
                  指定只能單機(jī)使用的可寫數(shù)據(jù)的安裝位置.

                --libdir=DIR
                  指定庫文件的安裝位置.

                --includedir=DIR
                  指定C頭文件的安裝位置.其他語言如C++的頭文件也可以使用此選項(xiàng).

                --oldincludedir=DIR
                  指定為除GCC外編譯器安裝的C頭文件的安裝位置.

                --infodir=DIR
                  指定Info格式文檔的安裝位置.Info是被GNU工程所使用的文檔格式.

                --mandir=DIR
                  指定手冊(cè)頁的安裝位置.

                --srcdir=DIR
                  這個(gè)選項(xiàng)對(duì)安裝沒有作用.他會(huì)告訴'configure'源碼的位置.一般來說不用指定此選項(xiàng),因?yàn)?configure'腳本一般和源碼文件在同一個(gè)目錄下.

                --program-prefix=PREFIX
                  指定將被加到所安裝程序的名字上的前綴.例如,使用'--program-prefix=g'來configure一個(gè)名為'tar'的程序?qū)?huì)使安裝的程序被命名為'gtar'.當(dāng)和其他的安裝選項(xiàng)一起使用時(shí),這個(gè)選項(xiàng)只有當(dāng)他被`Makefile.in'文件使用時(shí)才會(huì)工作.

                --program-suffix=SUFFIX
                  指定將被加到所安裝程序的名字上的后綴.

                --program-transform-name=PROGRAM
                  這里的PROGRAM是一個(gè)sed腳本.當(dāng)一個(gè)程序被安裝時(shí),他的名字將經(jīng)過`sed -e PROGRAM'來產(chǎn)生安裝的名字.

                --build=BUILD
                  指定軟件包安裝的系統(tǒng)平臺(tái).如果沒有指定,默認(rèn)值將是'--host'選項(xiàng)的值.

                --host=HOST
                  指定軟件運(yùn)行的系統(tǒng)平臺(tái).如果沒有指定,將會(huì)運(yùn)行`config.guess'來檢測(cè).

                --target=GARGET
                  指定軟件面向(target to)的系統(tǒng)平臺(tái).這主要在程序語言工具如編譯器和匯編器上下文中起作用.如果沒有指定,默認(rèn)將使用'--host'選項(xiàng)的值.

                --disable-FEATURE
                  一些軟件包可以選擇這個(gè)選項(xiàng)來提供為大型選項(xiàng)的編譯時(shí)配置,例如使用Kerberos認(rèn)證系統(tǒng)或者一個(gè)實(shí)驗(yàn)性的編譯器最優(yōu)配置.如果默認(rèn)是提供這些特性,可以使用'--disable-FEATURE'來禁用它,這里'FEATURE'是特性的名字.例如:

               $ ./configure --disable-gui

                -enable-FEATURE[=ARG]
                  相反的,一些軟件包可能提供了一些默認(rèn)被禁止的特性,可以使用'--enable-FEATURE'來起用它.這里'FEATURE'是特性的名字.一個(gè)特性可能會(huì)接受一個(gè)可選的參數(shù).例如:
                $ ./configure --enable-buffers=128
                `--enable-FEATURE=no'與上面提到的'--disable-FEATURE'是同義的.

                --with-PACKAGE[=ARG]
                  在自由軟件社區(qū)里,有使用已有軟件包和庫的優(yōu)秀傳統(tǒng).當(dāng)用'configure'來配置一個(gè)源碼樹時(shí),可以提供其他已經(jīng)安裝的軟件包的信息.例如,倚賴于Tcl和Tk的BLT器件工具包.要配置BLT,可能需要給'configure'提供一些關(guān)于我們把Tcl和Tk裝的何處的信息:
                $ ./configure --with-tcl=/usr/local --with-tk=/usr/local
                '--with-PACKAGE=no'與下面將提到的'--without-PACKAGE'是同義的.

                --without-PACKAGE
                  有時(shí)候你可能不想讓你的軟件包與系統(tǒng)已有的軟件包交互.例如,你可能不想讓你的新編譯器使用GNU ld.通過使用這個(gè)選項(xiàng)可以做到這一點(diǎn):
                $ ./configure --without-gnu-ld

                --x-includes=DIR
                  這個(gè)選項(xiàng)是'--with-PACKAGE'選項(xiàng)的一個(gè)特例.在Autoconf最初被開發(fā)出來時(shí),流行使用'configure'來作為Imake的一個(gè)變通方法來制作運(yùn)行于X的軟件.'--x-includes'選項(xiàng)提供了向'configure'腳本指明包含X11頭文件的目錄的方法.

                --x-libraries=DIR
                  類似的,'--x-libraries'選項(xiàng)提供了向'configure'腳本指明包含X11庫的目錄的方法.

                  在源碼樹中運(yùn)行'configure'是不必要的同時(shí)也是不好的.一個(gè)由'configure'產(chǎn)生的良好的'Makefile'可以構(gòu)筑源碼屬于另一棵樹的軟件包.在一個(gè)獨(dú)立于源碼的樹中構(gòu)筑派生的文件的好處是很明顯的:派生的文件,如目標(biāo)文件,會(huì)凌亂的散布于源碼樹.這也使在另一個(gè)不同的系統(tǒng)或用不同的配置選項(xiàng)構(gòu)筑同樣的目標(biāo)文件非常困難.建議使用三棵樹:一棵源碼樹(source tree),一棵構(gòu)筑樹(build tree),一棵安裝樹(install tree).這里有一個(gè)很接近的例子,是使用這種方法來構(gòu)筑GNU malloc包:

                $ gtar zxf mmalloc-1.0.tar.gz

                $ mkdir build && cd build

                $ ../mmalloc-1.0/configure

                creating cache ./config.cache

                checking for gcc... gcc
                checking whether the C compiler (gcc ) works... yes

                checking whether the C compiler (gcc ) is a cross-compiler... no

                checking whether we are using GNU C... yes

                checking whether gcc accepts -g... yes

                checking for a BSD compatible install... /usr/bin/install -c

                checking host system type... i586-pc-linux-gnu

                checking build system type... i586-pc-linux-gnu

                checking for ar... ar
                checking for ranlib... ranlib

                checking how to run the C preprocessor... gcc -E

                checking for unistd.h... yes

                checking for getpagesize... yes

                checking for working mmap... yes

                checking for limits.h... yes

                checking for stddef.h... yes

                updating cache ../config.cache
                creating ./config.status

                這樣這棵構(gòu)筑樹就被配置了,下面可以繼續(xù)構(gòu)筑和安裝這個(gè)包到默認(rèn)的位置'/usr/local':

                $ make all && make install

            一個(gè)軟件包通過編譯源代碼安裝后,如何完全的卸載??

                如果原先的source還在的話,很多source的Makefile都有寫uninstall規(guī)則,直接在Souce里make uninstall就可行,不過碰到無良作者沒寫的,那一句一句看Makefile里install部分他都干了些什么,然后挨個(gè)刪除。
                如果source沒了.....那就一邊郁悶吧
                到目前為止, 我裝的都可以make uninstall.......
                (因?yàn)榭偸遣恍⌒难b錯(cuò)地方, 結(jié)果就make uninstall&&make clean,然后重新configure......)
                linux下軟件的基本安裝和卸載

                Linux軟件的安裝和卸載一直是困擾許多新用戶的難題。在Windows中,我們可以使用軟件自帶的安裝卸載程序或在控制面板中的“添加/刪除程序”來實(shí)現(xiàn)。與其相類似,在Linux下有一個(gè)功能強(qiáng)大的軟件安裝卸載工具,名為RPM。它可以用來建立、安裝、查詢、更新、卸載軟件。該工具是在命令行下使用的。在Shell的提示符后輸入rpm,就可獲得該命令的幫助信息。

                軟件的安裝

                Linux下軟件的安裝主要有兩種不同的形式。第一種安裝文件名為xxx.tar.gz;另一種安裝文件名為xxx.i386.rpm。以第一種方式發(fā)行的軟件多為以源碼形式發(fā)送的;第二種方式則是直接以二進(jìn)制形式發(fā)送的。

                對(duì)于第一種,安裝方法如下:

                1 .首先,將安裝文件拷貝至你的目錄中。例如,如果你是以root身份登錄上的,就將軟件拷貝至/root中。

                #cp xxx.tar.gz /root

                2 .由于該文件是被壓縮并打包的,應(yīng)對(duì)其解壓縮。命令為:

                #tar xvzf filename.tar.gz 如果是filename.tar.bz2格式的,應(yīng)該是tar jxvf filename.tar.bz2來解壓

                3. 執(zhí)行該命令后,安裝文件按路徑,解壓縮在當(dāng)前目錄下。用ls命令可以看到解壓縮后的文件。通常在解壓縮后產(chǎn)生的文件中,有“Install”的文件。該文件為純文本文件,詳細(xì)講述了該軟件包的安裝方法。

                4.執(zhí)行解壓縮后產(chǎn)生的一個(gè)名為configure的可執(zhí)行腳本程序。它是用于檢查系統(tǒng)是否有編譯時(shí)所需的庫,以及庫的版本是否滿足編譯的需要等安裝所需要的系統(tǒng)信息。為隨后的編譯工作做準(zhǔn)備。命令為: #./configure

                如果您想把軟件安裝到指定目錄,應(yīng)該用#./configure --prefix=/您自己指定的目錄,比如我想把一個(gè)mlterm安裝到/opt/mlterm目錄中,應(yīng)該如下輸入

                #./configure --prefix=/opt/mlterm

                5.檢查通過后,將生成用于編譯的MakeFile文件。此時(shí),可以開始進(jìn)行編譯了。編譯的過程視軟件的規(guī)模和計(jì)算機(jī)性能的不同,所耗費(fèi)的時(shí)間也不同。命令為: #make。

                6.成功編譯后,鍵入如下的命令開始安裝:

                #make install

                7.安裝完畢,應(yīng)清除編譯過程中產(chǎn)生的臨時(shí)文件和配置過程中產(chǎn)生的文件。鍵入如下命令:

                #make clean

                #make distclean

                至此,軟件的安裝結(jié)束。

                對(duì)于第二種,其安裝方法要簡(jiǎn)單得多。

                同第一種方式一樣,將安裝文件拷貝至你的目錄中。然后使用rpm來安裝該文件。命令如下:

                #rpm -i filename.i386.rpm

                rpm將自動(dòng)將安裝文件解包,并將軟件安裝到缺省的目錄下。并將軟件的安裝信息注冊(cè)到rpm的數(shù)據(jù)庫中。參數(shù)i的作用是使rpm進(jìn)入安裝模式。

                軟件的卸載

                1.軟件的卸載主要是使用rpm來進(jìn)行的。卸載軟件首先要知道軟件包在系統(tǒng)中注冊(cè)的名稱。鍵入命令:

                #rpm -q -a

                即可查詢到當(dāng)前系統(tǒng)中安裝的所有的軟件包。

                2. 確定了要卸載的軟件的名稱,就可以開始實(shí)際卸載該軟件了。鍵入命令:

                #rpm -e [package name]

                即可卸載軟件。參數(shù)e的作用是使rpm進(jìn)入卸載模式。對(duì)名為[package name]的軟件包進(jìn)行卸載。由于系統(tǒng)中各個(gè)軟件包之間相互有依賴關(guān)系。如果因存在依賴關(guān)系而不能卸載,rpm將給予提示并停止卸載。你可以使用如下的命令來忽略依賴關(guān)系,直接開始卸載:

                #rpm -e [package name] -nodeps

                忽略依賴關(guān)系的卸載可能會(huì)導(dǎo)致系統(tǒng)中其它的一些軟件無法使用

                如果想知道rpm包安裝到哪里了呢?

                應(yīng)該用 #rpm -ql [package name]

                3.如何卸載用源碼包安裝的軟件?

                最好是看README和INSTALL ;一般的情況下都有說,但大多軟件沒有提供源碼包的卸載方法;我們可以找到軟件的安裝點(diǎn)刪除。主要看你把它安裝在哪了。

                比如:

                如果安裝軟件時(shí),指定個(gè)目錄。這個(gè)問題也不會(huì)難;

                比如用源碼包安裝gaim 的

                #./configure --prefix=/opt/gaim

                #make

                #make install

                如果安裝mlterm

                #./configure --prefix=/opt/mlterm

                #make

                #make install

                把源碼包安裝的軟件,都指定安裝在 /opt目錄中,這樣不就知道了??

                如果刪除,就刪除相應(yīng)的軟件目錄;

                有些軟件要在解壓安裝目錄中執(zhí)行 make uninstall ,這樣就卸載掉了

            posted @ 2009-08-06 15:26 chaosuper 閱讀(129) | 評(píng)論 (0)編輯 收藏

               用VC來寫程序,有時(shí)總是出這樣那樣的問題,沒辦法只能自己上網(wǎng)查資料來解決,在這里把自己常見的問題和一些技巧貼出來分享給大家,希望對(duì)大家有用,也省去大家再去搜索的煩惱……

                1.如何在Release狀態(tài)下進(jìn)行調(diào)試

                Project->Setting=>ProjectSetting對(duì)話框,選擇Release狀態(tài)。C/C++標(biāo)簽中的Category選General,Optimizations選Disable(Debug),Debut info選Program Database.在Link標(biāo)簽中選中Generate debug info復(fù)選框。

                注:只是一個(gè)介乎Debug和Release的中間狀態(tài),所有的ASSERT、VERIFY都不起作用,函數(shù)調(diào)用方式已經(jīng)是真正的調(diào)用,而不查表,但是這種狀態(tài)下QuickWatch、調(diào)用隊(duì)列跟蹤功能仍然有效,和Debug版一樣。

                2. Release和Debug有什么不同

                Release版稱為發(fā)行版,Debug版稱為調(diào)試版。

                Debug中可以單步執(zhí)行、跟蹤等功能,但生成的可執(zhí)行文件比較大,代碼運(yùn)行速度較慢。Release版運(yùn)行速度較快,可執(zhí)行文件較小,但在其編譯條件下無法執(zhí)行調(diào)試功能。

                Release的exe文件鏈接的是標(biāo)準(zhǔn)的MFC DLL(Use MFC in a shared or static dll)。這些DLL在安裝Windows的時(shí)候,已經(jīng)配置,所以這些程序能夠在沒有安裝Visual C++ 6.0的機(jī)器上運(yùn)行。而Debug版本的exe鏈接了調(diào)試版本的MFC DLL文件,在沒有安裝Visual C++6.0的機(jī)器上不能運(yùn)行,因?yàn)槿毕鄳?yīng)的DLL,除非選擇use static dll when link.

                3. ASSERT和VERIFY有什么區(qū)別

                ASSERT里面的內(nèi)容在Release版本中不編譯,VERIFY里面的內(nèi)容仍然翻譯,但不再判斷真假。所以后者更安全一點(diǎn)。例如ASSERT(file.Open(strFileName))。一旦到了Release版本中,這一行就忽略了,file根本就不Open()了,而且沒有任何出錯(cuò)的信息。如果用VERIFY()就不會(huì)有這個(gè)問題。

                4.Workspace和Project之間是什么樣的關(guān)系

                每個(gè)Workspace可以包括幾個(gè)project,但只有一個(gè)處于Active狀態(tài),各個(gè)project之間可以有依賴關(guān)系,在project的Setting……中可以設(shè)定,比如那個(gè)Active狀態(tài)的project可以依賴于其他的提供其函數(shù)調(diào)用的靜態(tài)庫。

                5. 如何在非MFC程序中使用ClassWizard

                在工程目錄下新建一個(gè)空的。RC文件,然后加入到工程中就可以了。

                6.如何設(shè)置斷點(diǎn)

                按F9在當(dāng)前光標(biāo)處增加一個(gè)斷點(diǎn)和取消一個(gè)斷點(diǎn)。另外,在編輯狀態(tài)下,按Ctrl+B組合鍵,彈出斷點(diǎn)設(shè)置對(duì)話框。然后單擊「Condition…」按鈕彈出設(shè)置斷點(diǎn)條件的對(duì)話框進(jìn)行設(shè)置。

                7.在編輯狀態(tài)下發(fā)現(xiàn)成員變量或函數(shù)不能顯示提示是如何打開顯示功能

                這似乎是目前這個(gè)Visual C++ 6.0版本的一個(gè)bug,可按如下步驟使其正常,如再出現(xiàn),可如法炮制:

                (1)關(guān)閉Project

                (2)刪除“工程名。ncb”文件

                (3)重新打開工程

                8.如何將一個(gè)通過ClassWizard生成的類徹底刪除

                首先在工作區(qū)的FileView中選中該類的。h和。cpp文件,按delete刪除,然后在文件管理器中將這兩個(gè)文件刪除,再運(yùn)行ClassWizard,這時(shí)出現(xiàn)是否移走該類的提示,選擇remove就可以了。

                9. 如何將在workspace中消失的類找出來

                打開該類對(duì)應(yīng)的頭文件,然后將其類名隨便改一下,這個(gè)時(shí)候工作區(qū)就會(huì)出現(xiàn)新的類,再將這個(gè)類改回原來的名字就可以了。

                10. 如何清除所有的斷點(diǎn)

                菜單「Edit」->「Breakpoints…」,打開“Breakpoints”對(duì)話框,單擊「Remove All」按鈕即可。快捷鍵是“Ctrl + Shift + F8”。

                11. 如何再ClassWizard中選擇未列出的信息

                打開“ClassWizard”對(duì)話框,然后切換到“Class Info”頁面。改變“Message filter”,如選擇“Window”,“Message”頁面就會(huì)出現(xiàn)Window的信息。

                12. 如何檢測(cè)程序中的括號(hào)是否匹配

                把光標(biāo)移動(dòng)到需要檢測(cè)的括號(hào)前面,按快捷鍵“Ctrl + ]”。如果括號(hào)匹配正確,光標(biāo)就跳到匹配的括號(hào)處,否則光標(biāo)不移動(dòng),并且機(jī)箱喇叭還會(huì)發(fā)出一聲警告。

                13. 如何查看一個(gè)宏(或變量、函數(shù))的定義

                把光標(biāo)移動(dòng)到要查看的一個(gè)宏上,就比如說最常見的DECLARE_MAP_MESSAGE上按一下F12(或右鍵菜單中的相關(guān)菜單),如果沒有建立瀏覽文件,就會(huì)出現(xiàn)提示對(duì)話框,按「確定」按鈕,然后就會(huì)跳到該宏(或變量、函數(shù))定義的地方。

                14. 如何添加Lib文件到當(dāng)前工程

                單擊菜單「Project」->「Settings…」彈出“Project Setting”對(duì)話框,切換到“Link”標(biāo)簽頁,在“Object/library modules”處輸入Lib文件名稱,不同的Lib之間用空格格開。

                15. 如何快速刪除項(xiàng)目下的Debug文件夾中臨時(shí)文件

                在工作區(qū)的FileView視圖中選中對(duì)應(yīng)的項(xiàng)目,單擊右鍵彈出菜單,選擇「Clean(selection only)」菜單即可。

                16. 如何快速生成一個(gè)現(xiàn)有工程除了工程名外完全相同的新工程

                在新建工程的“New”對(duì)話框中選擇“Custom Appwizard”項(xiàng),輸入新工程的名字,單擊「OK」按鈕。出現(xiàn)“Custom AppWizard”項(xiàng),輸入新工程的名字,單擊「OK」按鈕。出現(xiàn)“Custom AppWizard-Step 1 of 2”對(duì)話框,選擇“An existing Project”項(xiàng),單擊「Next」按鈕。出現(xiàn)“Custom AppWizard-Step 2 of 2”對(duì)話框,選擇現(xiàn)有工程的工程文件名,最后單擊「Finish」按鈕。編譯后就生成一個(gè)與現(xiàn)有工程相同但可以重新取名的工程AppWizard.現(xiàn)在就可以項(xiàng)用MFC AppWizard一樣用這個(gè)定制的向?qū)АH绻幌胗昧耍梢栽赩isual C++ 6.0安裝目錄下Common\MSDev98\Template目錄中刪除該Wizard對(duì)應(yīng)的。awx和。pdb文件。

                17. 如何解決Visual C++ 6.0不正確連接的問題

                情景:明明改動(dòng)了一個(gè)文件,卻要把整個(gè)項(xiàng)目全部重新編譯鏈接一次。剛剛鏈接好,一運(yùn)行,又提示重新編譯鏈接一次。這是因?yàn)槌霈F(xiàn)了未來文件(修改時(shí)間和創(chuàng)建時(shí)間比系統(tǒng)時(shí)間晚)的緣故。可以這樣處理:找到工程文件夾下的debug目錄,將創(chuàng)建和修改時(shí)間都比系統(tǒng)時(shí)間的文件全部刪除,然后再從新“Rebuild All”一次。

                18. 引起LNK2001的常見錯(cuò)誤都有哪些

                遇到的LNK2001錯(cuò)誤主要為:unresolved external symbol “symbol”,如果鏈接程序不能在所有的庫和目標(biāo)文件內(nèi)找到所引用的函數(shù)、變量或標(biāo)簽,將產(chǎn)生此錯(cuò)誤信息。

                一般來說,發(fā)生錯(cuò)誤的原因有兩個(gè):一是所引用的函數(shù)、變量不存在,拼寫不正確或者使用錯(cuò)誤;其次可能使用了不同版本的鏈接庫。以下是可能產(chǎn)生LNK2001錯(cuò)誤的原因:

                <1>由于編碼錯(cuò)誤導(dǎo)致的LNK2001錯(cuò)誤

                (1)不相匹配的程序代碼或模塊定義(。DEF)文件導(dǎo)致LNK2001.例如,如果在C++源文件了內(nèi)聲明了一變量“var1”,卻試圖在另一個(gè)文件內(nèi)以變量“var1”訪問改變量。

                (2)如果使用的內(nèi)聯(lián)函數(shù)是在。cpp文件內(nèi)定義的,而不是在頭文件內(nèi)定義將導(dǎo)致LNK2001錯(cuò)誤。

                (3)調(diào)用函數(shù)時(shí)如果所用的參數(shù)類型和頭函數(shù)聲明時(shí)的類型不符將會(huì)產(chǎn)生LNK2001錯(cuò)誤。

                (4)試圖從基類的構(gòu)造函數(shù)或析構(gòu)函數(shù)中調(diào)用虛擬函數(shù)時(shí)將會(huì)導(dǎo)致LNK2001錯(cuò)誤。

                (5)要注意函數(shù)和變量的可公用性,只有全局變量、函數(shù)是可公用的。靜態(tài)函數(shù)和靜態(tài)變量具有相同的使用范圍限制。當(dāng)試圖從文件外部方位任何沒有在該文件內(nèi)聲明的靜態(tài)變量時(shí)將導(dǎo)致編譯錯(cuò)誤或LNK2001錯(cuò)誤。

                <2>由于編譯和聯(lián)機(jī)的設(shè)置而造成的LNK2001錯(cuò)誤

                (1)如果編譯時(shí)使用的是/NOD(/NODERAULTLIB)選項(xiàng),程序所需要的運(yùn)行庫和MFC時(shí)將得到又編譯器寫入目標(biāo)文件模塊,但除非在文件中明確包含這些庫名,否則這些庫不會(huì)被鏈接進(jìn)工程文件。這種情況下使用/NOD將導(dǎo)致LNK2001錯(cuò)誤

                (2)如果沒有為wWinMainCRTStartup設(shè)定程序入口,在使用Unicode和MFC時(shí)將出現(xiàn)“unresolved external on _WinMain@16”的LNK2001錯(cuò)誤信息。

                (3)使用/MD選項(xiàng)編譯時(shí),既然所有的運(yùn)行庫都被保留在動(dòng)態(tài)鏈接庫之內(nèi),源文件中對(duì)“func”的引用,在目標(biāo)文件里即對(duì)“__imp__func”的引用。如果試圖使用靜態(tài)庫LIBC.LIB或LIBCMT.LIB進(jìn)行鏈接,將在__imp__func上發(fā)生LNK2001錯(cuò)誤。如果不使用/MD選項(xiàng)編譯,在使用MSVCxx.LIB鏈接時(shí)也會(huì)發(fā)生LNK2001錯(cuò)誤。

                (4)使用/ML選項(xiàng)編譯時(shí),如用LIBCMT.LIB鏈接會(huì)在_errno上發(fā)生LNK2001錯(cuò)誤。

                (5)當(dāng)編譯調(diào)試版的應(yīng)用程序時(shí),如果采用發(fā)行版模態(tài)庫進(jìn)行鏈接也會(huì)產(chǎn)生LNK2001錯(cuò)誤;同樣,使用調(diào)試版模態(tài)庫鏈接發(fā)行版應(yīng)用程序時(shí)也會(huì)產(chǎn)生相同的錯(cuò)誤。

                (6)不同版本的庫和編譯器的混合使用也能產(chǎn)生問題,因?yàn)樾掳娴膸炖锟赡馨缦鹊陌姹緵]有的符號(hào)和說明。

                (7)在不同的模塊中使用內(nèi)聯(lián)和非內(nèi)聯(lián)的編譯選項(xiàng)能夠?qū)е翷NK2001錯(cuò)誤。如果創(chuàng)建C++庫時(shí)打開了函數(shù)內(nèi)聯(lián)(/Ob1或/Ob2),但是在描述該函數(shù)的相應(yīng)頭文件里卻關(guān)閉了函數(shù)內(nèi)聯(lián)(沒有inline關(guān)鍵字),只是將得到錯(cuò)誤信息。為避免該問題的發(fā)生,應(yīng)該在相應(yīng)的頭文件中用inline關(guān)鍵字標(biāo)志為內(nèi)聯(lián)函數(shù)。

                (8)不正確的/SUBSYSTEM或ENTRY設(shè)置也能導(dǎo)致LNK2001錯(cuò)誤。

                19. 如何調(diào)試一個(gè)沒有源碼的exe文件調(diào)用的dll

                在Visual C++ 6.0中,進(jìn)入“Project Setting”對(duì)話框然后選擇Debug標(biāo)簽頁。通常Visual Studio默認(rèn)“executable for debug session”為可執(zhí)行文件名,但可以將他改成任何你想要的程序。甚至可以指定不同的工作目錄以及傳遞參數(shù)到你的程序。這個(gè)技術(shù)常用來調(diào)試Dlls、名字空間擴(kuò)展、COM對(duì)象和其他從某些EXE以及從第三方的EXE中調(diào)用的plug-in程序。

                20.   Visual C++ 6.0工程中的項(xiàng)目文件都表示什么

                ·opt:工程關(guān)于開發(fā)環(huán)境的參數(shù)文件。如工具條位置等信息。

                ·aps(AppStudio File)資源輔助文件,二進(jìn)制格式,一般不用去管它。

                ·clw:ClassWizard信息文件,實(shí)際上是INI文件格式,有興趣可以研究一下。有時(shí)候ClassWizard出了問題,手工修改CLW文件可以解決。如果此文件不存在的話,每次用ClassWizard的時(shí)候回提示是否重建。

                ·dsp(DevelopStudio Project):項(xiàng)目文件,文本格式,不過不熟悉的不要手工修改。

                ·dsw(DevelopStudio Workspace):是工作區(qū)文件,其他特點(diǎn)和。dsp差不多。

                ·plg:是編譯信息文件,編譯時(shí)的error和warning信息文件(實(shí)際上是一個(gè)html文件),一般用處不大。在單擊菜單「Tool」->「Option」彈出的對(duì)話框里面有個(gè)選項(xiàng)可以控制這個(gè)文件的生成。

                ·hpj(Help Project):是生成幫助文件的工程,用microsoft Help Compiler可以處理。

                ·mdp(Microsoft DevStudio Project):是舊版本的項(xiàng)目文件,如果要打開此文件的話,會(huì)提示你是否轉(zhuǎn)換成新的。dsp格式。

                ·bsc:是用于瀏覽項(xiàng)目信息的,如果用Source Brower的話就必須有這個(gè)文件。如果不用這個(gè)功能的話,可以在Project Options里面去掉Generate Browse Info File,這樣可以加快編譯速度。

                ·map是執(zhí)行文件的映象信息記錄文件,除非對(duì)系統(tǒng)底層,這個(gè)文件一般用不著。

                ·pch(Pre-Compiled File):是與編譯文件,可以加快編譯速度,但是文件非常大。

                ·pdb(Program Database):記錄了程序有關(guān)的一些數(shù)據(jù)和調(diào)試信息,在調(diào)試的時(shí)候可能有用。

                ·exp:只有在編譯DLL的時(shí)候才會(huì)生成,記錄了DLL文件的一些信息,一般也沒有用。

                ·ncb:無編譯瀏覽文件(no compile browser)。當(dāng)自動(dòng)完成功能出問題時(shí)可以刪除此文件。編譯工程后會(huì)自動(dòng)生成。

            posted @ 2009-08-06 15:24 chaosuper 閱讀(138) | 評(píng)論 (0)編輯 收藏

                1. 學(xué)會(huì)寫簡(jiǎn)單的makefile

                2. 編一應(yīng)用程序,可以用makefile跑起來

                3. 學(xué)會(huì)寫驅(qū)動(dòng)的makefile

                4. 寫一簡(jiǎn)單char驅(qū)動(dòng),makefile編譯通過,可以insmod, lsmod, rmmod. 在驅(qū)動(dòng)的init函數(shù)里打印hello world, insmod后應(yīng)該能夠通過dmesg看到輸出。

                5. 寫一完整驅(qū)動(dòng), 加上read, write, ioctl, polling等各種函數(shù)的驅(qū)動(dòng)實(shí)現(xiàn)。 在ioctl里完成從用戶空間向內(nèi)核空間傳遞結(jié)構(gòu)體的實(shí)現(xiàn)。

                6. 寫一block驅(qū)動(dòng), 加上read,write,ioctl,poll等各種函數(shù)實(shí)現(xiàn)。

                7. 簡(jiǎn)單學(xué)習(xí)下內(nèi)存管理, 這個(gè)是最難的,明白各種memory alloc的函數(shù)實(shí)現(xiàn)細(xì)節(jié)。這是Linux開發(fā)的基本功。

                8. 學(xué)習(xí)鎖機(jī)制的應(yīng)用,這個(gè)不是最難的但是最容易犯錯(cuò)的,涉及到很多同步和并發(fā)的問題。

                9. 看內(nèi)核中實(shí)際應(yīng)用的驅(qū)動(dòng)代碼。 你會(huì)發(fā)現(xiàn)最基本的你已經(jīng)知道了, 大的框架都是一樣的, 無非是read, write, ioctl等函數(shù)的實(shí)現(xiàn), 但里面包含了很多很多細(xì)小的實(shí)現(xiàn)細(xì)節(jié)是之前不知道的。 這時(shí)候就要考慮到很多別的問題而不僅僅是基本功能的實(shí)現(xiàn)。

                推薦您看2.6.20中integrated的一個(gè)驅(qū)動(dòng) kvm, 記得是在driver/lguest下,很好玩的, 就是Linux下的虛擬機(jī)驅(qū)動(dòng), 代碼不長(zhǎng),但功能強(qiáng)大。有能力的可以自己寫一操作系統(tǒng)按照要求做成磁盤鏡像加載到虛擬機(jī)中, 然后客戶機(jī)可以有自己的4G虛擬地址空間。

                10. 看完驅(qū)動(dòng)歡迎您進(jìn)入Linux kernel學(xué)習(xí)中來。

                最簡(jiǎn)單的方法,跟著ldd(Linux devive driver)做一遍。

            posted @ 2009-08-06 06:16 chaosuper 閱讀(181) | 評(píng)論 (0)編輯 收藏

            Ubuntu 在安裝時(shí),如同大部分 Linux 發(fā)行版一樣,都會(huì)同時(shí)安裝 GNU 版本的 Java。這個(gè) Java 的實(shí)用程度太低,尤其對(duì)于開發(fā)人員來說,是沒有太多用處的。在 Ubuntu 下,安裝 SUN Java 是一件很容易的事情。第一步:
                sudo apt-get install sun-java5-jdk
            安裝完畢之后,選擇默認(rèn) java:
                sudo update-alternatives --config java
            然后配置環(huán)境變量:
                sudo vim /etc/environment
            在其中添加如下兩行:
                CLASSPATH=/usr/lib/jvm/java-1.5.0-sun/lib
                JAVA_HOME=/usr/lib/jvm/java-1.5.0-sun
            保存退出。

            之后安裝配置 Eclipse。安裝很簡(jiǎn)單:
                sudo apt-get install eclipse
            然已經(jīng)這時(shí)新安裝的 java 已經(jīng)成為系統(tǒng)默認(rèn)的 jvm,但是 Eclipse 并不會(huì)用 update-alternative 設(shè)置的 jvm 來啟動(dòng)自身,而使用的是以前的 GNU Java。GNU Java 是 1.4.2 的實(shí)現(xiàn),而且在性能上遠(yuǎn)不如 SUN 的實(shí)現(xiàn)。為了讓 Eclipse 利用 SUN Java 啟動(dòng),我們還需要繼續(xù)配置。首先將 SUN Java 完完全全的設(shè)置為系統(tǒng)的默認(rèn) JDK:
                sudo update-java-alternatives -s java-1.5.0-sun
            然后編輯 JVM 配置文件:
                sudo vim /etc/jvm
            將文件中的
                /usr/lib/jvm/java-1.5.0-sun
            這一行移動(dòng)到配置塊的頂部。由于 Eclipse 會(huì)忽略 Ubuntu 的通用 Java 設(shè)置(貌似一個(gè) bug),我們需要繼續(xù)編輯 Eclipse 的 java_home 文件:
                sudo vim /etc/eclipse/java_home
            如同上面一樣,將
                /usr/lib/jvm/java-1.5.0-sun
            這一行移動(dòng)到文件的頂部。

            所有的安裝配置完成之后,Ubuntu 的 Java 開發(fā)平臺(tái)就基本完備了。
            posted @ 2009-08-06 05:29 chaosuper 閱讀(71) | 評(píng)論 (0)編輯 收藏

                C++虛函數(shù)探索筆記(3)——延伸思考:虛函數(shù)應(yīng)用的一些其他情形

                關(guān)注問題:

                虛函數(shù)的作用

                虛函數(shù)的實(shí)現(xiàn)原理

                虛函數(shù)表在對(duì)象布局里的位置

                虛函數(shù)的類的sizeof

                純虛函數(shù)的作用

                多級(jí)繼承時(shí)的虛函數(shù)表內(nèi)容

                虛函數(shù)如何執(zhí)行父類代碼

                多繼承時(shí)的虛函數(shù)表定位,以及對(duì)象布局

                虛析構(gòu)函數(shù)的作用

                虛函數(shù)在QT中的應(yīng)用

                虛函數(shù)與inline修飾符,static修飾符

                虛析構(gòu)函數(shù)

                大家都知道,在C++里需要自己嚴(yán)格管理好資源的分配和回收。通常情況下,

            在一個(gè)對(duì)象被析構(gòu)的時(shí)候,是要由其釋放其申請(qǐng)到的各種資源的。最常見的,當(dāng)

            然就是內(nèi)存資源啦。

                當(dāng)只有一個(gè)類的時(shí)候,我們可以不用考慮太多,只要在析構(gòu)函數(shù)里檢查并釋

            放所有申請(qǐng)到的資源即可。但是在這個(gè)類繼承了一個(gè)抽象接口基類時(shí),就有點(diǎn)點(diǎn)

            不一樣了。讓我們看看類的析構(gòu)過程:

                在大多數(shù)的類的使用時(shí),通常都是直接刪除該類的實(shí)例對(duì)象,然后該類的析

            構(gòu)函數(shù)就會(huì)被調(diào)用,從而使得這個(gè)類在析構(gòu)函數(shù)里執(zhí)行的資源釋放代碼被執(zhí)行到

                如果這個(gè)類繼承了其他類,那么編譯器還會(huì)在這個(gè)類的析構(gòu)函數(shù)里自動(dòng)添加

            對(duì)父類的析構(gòu)函數(shù)的調(diào)用,從而將父類里申請(qǐng)的資源也進(jìn)行釋放。如果偶多個(gè)父

            類,也會(huì)依次調(diào)用各個(gè)析構(gòu)函數(shù)。

                倘若繼承的是一個(gè)抽象接口類,并且在程序運(yùn)行期,可能通過一個(gè)基類指針

            將此對(duì)象釋放掉,那么致命而又隱藏的內(nèi)存泄露BUG就出現(xiàn)啦……因?yàn)樵噲D刪除的

            是基對(duì)象,刪除時(shí)調(diào)用的是基類的析構(gòu)函數(shù),而基類的析構(gòu)函數(shù)當(dāng)然是不會(huì)去調(diào)

            用子類的析構(gòu)函數(shù)的羅!

                讓我們看看下面的代碼,使用vs2008編譯并運(yùn)行的時(shí)候,將會(huì)在程序運(yùn)行結(jié)

            束時(shí)報(bào)告內(nèi)存泄漏情況(如果要在linux下編譯測(cè)試,需要去掉第一行的include

            ,以及return前的_CrtDumpMemoryLeaks()函數(shù),然后使用linux下檢查內(nèi)存泄

            露的工具進(jìn)行測(cè)試)。
             //Source filename: Win32Con.cpp
            #include
            class parent
            {
            public:
             parent() { }
             /*virtual */ ~parent() { }
            };

            class child:public parent
            {
            public:
             child()
             {
              p=new char[1000];
             }
             ~child()
             {
              delete[] p;
             }
             char *p;
            };

            void free_child(parent *pp)
            {
             delete pp;
            }

            int main()
            {
             child *obj=new child();
             free_child(obj);
             _CrtDumpMemoryLeaks();
             return 0;
            }

             


                在這段代碼里我們創(chuàng)建的是一個(gè)child類型的對(duì)象,然后使用free_child

            (parent*)函數(shù)來試圖釋放這個(gè)對(duì)象,這個(gè)時(shí)候,只會(huì)調(diào)用到parent::

            ~parent()這個(gè)析構(gòu)函數(shù),而不會(huì)調(diào)用到child::~child()!

                如何解決這個(gè)問題呢?

                很簡(jiǎn)單的,只要在parent::~parent()前增加 virtual關(guān)鍵字,將其變成

            一個(gè)虛函數(shù)。這樣,無論是以這個(gè)對(duì)象的父類指針進(jìn)行刪除的時(shí)候,就會(huì)從虛函

            數(shù)表里定位到子類child的析構(gòu)函數(shù),這樣就能夠從子類開始一級(jí)一級(jí)的向上調(diào)用

            析構(gòu)函數(shù),從而正確的將這個(gè)對(duì)象在各個(gè)繼承層次上申請(qǐng)的所有資源都釋放掉。

                正因?yàn)檫@個(gè)原因,在很多C++編程原則的文章或者書里都會(huì)提到這樣的原則:

                如果一個(gè)類要被設(shè)計(jì)為可被繼承的基類,那么其析構(gòu)函數(shù)應(yīng)該被聲明為虛函

            數(shù)。

               虛函數(shù)在QT中的應(yīng)用

                在QT里虛函數(shù)的應(yīng)用非常的廣泛,事實(shí)上,在大多數(shù)的C++類庫里都不可避免

            的要使用到虛函數(shù)。這里簡(jiǎn)單的列舉QT里使用虛函數(shù)的情況:

                QT的事件機(jī)制

                是使用了虛函數(shù)的,你因此才可以自定義事件處理函數(shù)。比如最核心的

            QObject類的定義里(在qobject.h里),我們可以看到如下的虛函數(shù)定義:

                virtual bool event(QEvent *);

                virtual bool eventFilter(QObject *, QEvent *);

                然后,在QWidget類繼承QObject類后重新實(shí)現(xiàn)了上面的兩個(gè)虛函數(shù),完成很

            多窗口控件類的缺省事件處理。

                當(dāng)你要編寫自定義的QT控件的時(shí)候,對(duì)event虛函數(shù)的重新實(shí)現(xiàn)就更是重要啦

                QT的信號(hào)和槽

                QT的槽函數(shù)可以被聲明為虛函數(shù),所以雖然QT在實(shí)現(xiàn)信號(hào)和槽機(jī)制的時(shí)候可

            能出于效率或者運(yùn)行代價(jià)的原因未采用虛函數(shù)機(jī)制,但是我們依然可以在必要的

            時(shí)候使用虛函數(shù)來完成一些特定功能。比如為一些自定義控件類抽象出來一個(gè)抽

            象接口基類,在做信號(hào)和槽的連接的時(shí)候是對(duì)基類指針進(jìn)行操作,而在基類里的

            槽定義為虛函數(shù),那么虛函數(shù)在此依然可以實(shí)現(xiàn)信號(hào)與槽的多態(tài)。

                然而虛函數(shù)在調(diào)用的時(shí)候,一定要經(jīng)歷查表的步驟,是存在一定的運(yùn)行開銷

            的,對(duì)于一些非常頻繁的槽調(diào)用還是應(yīng)該考慮到使用虛函數(shù)產(chǎn)生的代價(jià)的。

                其他

                在虛函數(shù)上,static和inline這兩個(gè)關(guān)鍵詞與virtual顯得很不友好。

                從語義上即可看出,static和virtual完全就是沖突的,所以如果你試圖為一

            個(gè)虛函數(shù)增加一個(gè)static限定詞,那么你的C++編譯器就會(huì)很負(fù)責(zé)任的報(bào)告一個(gè)嚴(yán)

            重錯(cuò)誤給你。

                而inline的含義和虛函數(shù)其實(shí)也是非常沖突的,但是inline在語法上只是給

            編譯器一個(gè)建議,而不是強(qiáng)制的語義限定,所以C++編譯器應(yīng)該會(huì)忽略掉inline關(guān)

            鍵詞,繼續(xù)正常的編譯。

             

            posted @ 2009-08-05 17:47 chaosuper 閱讀(305) | 評(píng)論 (0)編輯 收藏

                多繼承與虛函數(shù)重復(fù)

                既然說到了多繼承,那么還有一個(gè)問題可能會(huì)需要解決,那就是如果兩個(gè)父類里都有相同的虛函數(shù)定義,在子對(duì)象的布局里會(huì)是怎么樣個(gè)情況?是否依然可以將這個(gè)虛函數(shù)指向到正確的實(shí)現(xiàn)代碼上呢?

                修改前面一個(gè)源代碼,在parent2的接口里增加下面的虛函數(shù)定義:

                virtual int fun1(){cout<<"parent2::fun1()"<<endl;return 0;};

                上面的fun1的定義與parent1類里的完全重復(fù)相同(類型,參數(shù)列表),增加上面的代碼后立即開始編譯,程序正常編譯通過。運(yùn)行之,得到下面的結(jié)果:

                child1::fun1()

                child1::fun2()

                這個(gè)程序居然正確的完成了執(zhí)行,編譯器在其中做了些怎樣的工作,是怎么樣避免掉隊(duì)fun1函數(shù)的沖突問題呢?

                讓我們來看看這個(gè)時(shí)候的child1的對(duì)象布局:
             class child1    size(8):
                    +---
                    | +--- (base class parent1)
             0      | | {vfptr}
                    | +---
                    | +--- (base class parent2)
             4      | | {vfptr}
                    | +---
                    +---

            child1::$vftable@parent1@:
                    | &child1_meta
                    |  0
             0      | &child1::fun1

            child1::$vftable@parent2@:
                    | -4
             0      | &child1::fun2
             1      | &thunk: this-=4; goto child1::fun1

            child1::fun1 this adjustor: 0
            child1::fun2 this adjustor: 4

             


                恩~~~還是兩個(gè)vfptr在child1的對(duì)象布局里(不一樣就怪啦,呵呵),但是第二個(gè)vfptr所指的虛函數(shù)表的內(nèi)容有所變化哦!

                注意看紅色字體部分,虛函數(shù)表里并沒有直接填寫child::fun1的代碼,而是多了一個(gè) &thunk: this-=4;然后才goto child1::fun1!注意到一個(gè)關(guān)鍵名詞thunk了吧?沒錯(cuò),vc在這里使用了名為thunk的技術(shù),避免了虛函數(shù)fun1在兩個(gè)基類里重復(fù)出現(xiàn)導(dǎo)致的沖突問題!(除了thunk,還有其他方法可以解決此類問題的)。

                現(xiàn)在,我們知道為什么相同的虛函數(shù)不會(huì)在子類里出現(xiàn)沖突的情況了。

                但是,倘若我們?cè)诨惱锞褪怯蓛蓚€(gè)沖突的普通函數(shù),而不是虛函數(shù),是個(gè)怎樣的情況呢?

                多繼承產(chǎn)生的沖突與虛繼承,虛基類

                我們?cè)趐arent1和parent2里添加一個(gè)相同的函數(shù)void fun3(),然后再進(jìn)行編譯,通過了!查看類對(duì)象布局,跟上面的完全一致。但是在main函數(shù)里調(diào)用chobj.fun3()的時(shí)候,編譯器卻不再能正確編譯了,并且會(huì)提示“error C2385: 對(duì)”fun3“的訪問不明確”的錯(cuò)誤信息,沒錯(cuò),編譯器不知道你要訪問哪個(gè)fun3了。

                如何解決這樣的多繼承帶來的問題呢,其實(shí)有一個(gè)簡(jiǎn)單的做法。就是在方法前限定引用的具體是哪個(gè)類的函數(shù),比如:chobj.parent1::fun3();  ,這樣的寫法就寫明了是要調(diào)用chobj的父類parent1里的fun3()函數(shù)!

                我們?cè)倏纯戳硗庖环N情況,從parent1和parent2里抹去剛才添加的fun3函數(shù),將之放到一個(gè)共同的基類里:
                 class commonbase

                {

                public:

                void fun3(){cout<<"commonbase::fun3()"<<endl;}

                };
             


                而parent1和parent2都修改為從此類繼承。可以看到,在這個(gè)情況下,依然需要使用chobj.parent1::fun3();  的方式才可以正確調(diào)用到fun3,難道,在這種情況下,就不能自然的使用chobj.fun3()這樣的方式了嗎?

                虛繼承可以解決這個(gè)問題——我們?cè)趐arent1和parent2繼承common類的地方添加上一個(gè)關(guān)鍵詞virtual,如下:
                 class parent1:virtual public commonbase

                {

                public:

                virtual int fun1(){cout<<"parent1::fun1()"<<endl;return 0;};

                };
             


                給parent2也同樣的處理,然后再次編譯,這次chobj.fun3()可以編譯通過了!!!

                編譯器這次又在私下里做了哪些工作了呢????
             class child1    size(16):
                    +---
                    | +--- (base class parent1)
             0      | | {vfptr}
             4      | | {vbptr}
                    | +---
                    | +--- (base class parent2)
             8      | | {vfptr}
            12      | | {vbptr}
                    | +---
                    +---
                    +--- (virtual base commonbase)
                    +---

            child1::$vftable@parent1@:
                    | &child1_meta
                    |  0
             0      | &child1::fun1

            child1::$vftable@parent2@:
                    | -8
             0      | &child1::fun2
             1      | &thunk: this-=8; goto child1::fun1

            child1::$vbtable@parent1@:
             0      | -4
             1      | 12 (child1d(parent1+4)commonbase)

            child1::$vbtable@parent2@:
             0      | -4
             1      | 4 (child1d(parent2+4)commonbase)

            child1::fun1 this adjustor: 0
            child1::fun2 this adjustor: 8

            vbi:       class  offset o.vbptr  o.vbte fVtorDisp
                  commonbase      16       4       4 0

             


                這次變化可大了去了!!!

                首先,可以看到兩個(gè)類parent1和parent2的對(duì)象布局里,都多了一個(gè)vbptr的指針。而在child1的對(duì)象布局里,還有一個(gè)virtual base commonbase的虛擬基類。再看看兩個(gè)vbptr的內(nèi)容:

                12 (child1d(parent1+4)commonbase) 這個(gè)很好理解,從parent1的vbptr開始,偏移12個(gè)字節(jié),指向的是virtual base commonbase!

                再看看4 (child1d(parent2+4)commonbase) ,從parent2的vbptr開始,便宜4個(gè)字節(jié),也指向了virtual base commonbase!

                這下明白了。虛基類在child1里只有一個(gè)共同的對(duì)象布局了,所以就可以直接用chobj.fun3()啦,當(dāng)然,在commonbase里的其他成員變量此時(shí)也可以同樣的方式訪問了!

                雖然解決方案有了,但是在一個(gè)系統(tǒng)的設(shè)計(jì)里,如果有一個(gè)基類出現(xiàn)多繼承沖突的情況,大部分情況下都說明這樣的設(shè)計(jì)是有問題的,應(yīng)該盡量避免這樣的設(shè)計(jì),并且盡量用純虛函數(shù),來提取一些抽象的接口類,把共同的方法接口都抽取出來,通常就能避免多繼承的問題。

             

            posted @ 2009-08-05 17:46 chaosuper 閱讀(145) | 評(píng)論 (0)編輯 收藏

              C++虛函數(shù)探索筆記(2)——虛函數(shù)與多繼承

                關(guān)注問題:

                虛函數(shù)的作用

                虛函數(shù)的實(shí)現(xiàn)原理

                虛函數(shù)表在對(duì)象布局里的位置

                虛函數(shù)的類的sizeof

                純虛函數(shù)的作用

                多級(jí)繼承時(shí)的虛函數(shù)表內(nèi)容

                虛函數(shù)如何執(zhí)行父類代碼

                多繼承時(shí)的虛函數(shù)表定位,以及對(duì)象布局

                虛析構(gòu)函數(shù)的作用

                虛函數(shù)在QT的信號(hào)與槽中的應(yīng)用

                虛函數(shù)與inline修飾符,static修飾符

                前面我們嘗試了一個(gè)簡(jiǎn)單的例子,接下來嘗試一個(gè)多級(jí)繼承的例子,以及一個(gè)多繼承的例子。主要涉及到以下問題:多級(jí)繼承時(shí)虛函數(shù)表的內(nèi)容是如何填寫的,如何在多級(jí)繼承的情況下調(diào)用某一級(jí)父類里的虛函數(shù),以及在多繼承(多個(gè)父類)的情況下的對(duì)象布局。

                多級(jí)繼承

                在這里,多級(jí)繼承指的是有3層或者多層繼承關(guān)系的情形。讓我們看看下面的代碼:
             //Source filename: Win32Con.cpp

             #include <iostream>
             using namespace std;
            class parent1
             {
            public:
                virtual int fun1(){cout<<"parent1::fun1()"<<endl;return 0;};
                virtual int fun2()=0;
            };
            class child1:public parent1
             {
            public:

                virtual int fun1()
                {
                    cout<<"child1::fun1()"<<endl;
                    parent1::fun1();
                    return 0;
                }
                virtual int fun2()
                {
                    cout<<"child1::fun2()"<<endl;
                    return 0;
                }
            };

            class grandson:public child1
             {
            public:
                virtual int fun2()
                {
                    cout<<"grandson::fun2()"<<endl;
                    //parent1::fun2();
                    parent1::fun1();
                    child1::fun2();
                    return 0;
                }
            };

            void test_func1(parent1 *pp)
            {
                pp->fun1();
                pp->fun2();
            }

            int main(int argc, char* argv[])
            {
                grandson sunzi;
                test_func1(&sunzi);
                return 0;

             


                這段代碼展示了三個(gè)class,分別是parent1,child1,grandson.

                類parent1定義了兩個(gè)虛函數(shù),其中fun2是一個(gè)純虛函數(shù),這個(gè)類是一個(gè)不可實(shí)例化的抽象基類。

                類child1繼承了parent1,并且對(duì)兩個(gè)虛函數(shù)fun1和fun2都編寫了實(shí)現(xiàn)的代碼,這個(gè)類可以被實(shí)例化。

                類grandson繼承了child1,但是只對(duì)虛函數(shù)fun2編寫了實(shí)現(xiàn)的代碼。

                此外,我們還改寫了test_func1函數(shù),它的參數(shù)為parent1類型的指針,我們可以將parent1的子孫類作為這個(gè)函數(shù)的參數(shù)傳入。在這個(gè)函數(shù)里,我們將依次調(diào)用parent1類的兩個(gè)虛函數(shù)。

                可以先通過閱讀代碼預(yù)測(cè)一下程序的輸出內(nèi)容。

                程序的輸出內(nèi)容將是:

                child1::fun1()

                parent1::fun1()

                grandson::fun2()

                parent1::fun1()

                child1::fun2()

                先看第一行輸出child1::fun1(),為什么會(huì)輸出它呢?我們定義的具體對(duì)象sunzi是grandson類型的,test_func1的參數(shù)類型是parent1類型。在調(diào)用這個(gè)虛函數(shù)的時(shí)候,是完成了一次怎樣的調(diào)用過程呢?

                讓我們?cè)俅问褂胏l命令輸出這幾個(gè)類的對(duì)象布局:
             class parent1   size(4):
                    +---
             0      | {vfptr}
                    +---

            parent1::$vftable@:
                    | &parent1_meta
                    |  0
             0      | &parent1::fun1
             1      | &parent1::fun2

            parent1::fun1 this adjustor: 0
            parent1::fun2 this adjustor: 0

            class child1    size(4):
                    +---
                    | +--- (base class parent1)
             0      | | {vfptr}
                    | +---
                    +---

            child1::$vftable@:
                    | &child1_meta
                    |  0
             0      | &child1::fun1
             1      | &child1::fun2

            child1::fun1 this adjustor: 0
            child1::fun2 this adjustor: 0

            class grandson  size(4): //grandson的對(duì)象布局
                    +---
                    | +--- (base class child1)
                    | | +--- (base class parent1)
             0      | | | {vfptr}
                    | | +---
                    | +---
                    +---

            grandson::$vftable@:  //grandson虛函數(shù)表的內(nèi)容
                    | &grandson_meta
                    |  0
             0      | &child1::fun1
             1      | &grandson::fun2

            grandson::fun2 this adjustor: 0

             


                因?yàn)槲覀儗?shí)例化的是一個(gè)grandson對(duì)象,讓我們看看它的對(duì)象布局。正如前面的例子一樣,里面只有一個(gè)vfptr指針,但是不一樣的卻是這個(gè)指針?biāo)傅奶摵瘮?shù)表的內(nèi)容:

                第一個(gè)虛函數(shù),填寫的是child1類的fun1的地址;第二個(gè)虛函數(shù)填寫的才是grandson類的fun2的地址。

                很顯然我們可以得出這樣一個(gè)結(jié)論:在一個(gè)子對(duì)象的虛函數(shù)表里,每一個(gè)虛函數(shù)的實(shí)際運(yùn)行的函數(shù)地址,將填寫為在繼承體系里最后實(shí)現(xiàn)該虛函數(shù)的函數(shù)地址。

                所以當(dāng)我們?cè)趖est_func1里調(diào)用了傳入的parent1指針的fun1函數(shù)的時(shí)候,我們實(shí)際執(zhí)行的是填寫在虛函數(shù)表里的child1::fun1(),而調(diào)用fun2函數(shù)的時(shí)候,是從虛函數(shù)表里得到了grandson::fun2函數(shù)的地址并調(diào)用之。在“程序輸出結(jié)果”表里的第一行和第三行結(jié)果證實(shí)了上述結(jié)論。

                再看一下程序代碼部分的child1::fun1()的實(shí)現(xiàn)代碼,在第18行,我們有parent1::fun1();這樣的語句,這行代碼輸出了運(yùn)行結(jié)果里的第二行,而在grandson::fun2()的實(shí)現(xiàn)代碼第35行的parent1::fun1();以及第36行的child1::fun2();則輸出了運(yùn)行結(jié)果里的第四行和第五行的內(nèi)容。這三行代碼展示了如何調(diào)用父類以及更高的祖先類里的虛函數(shù)。——事實(shí)上,這與調(diào)用父類的普通函數(shù)沒有任何區(qū)別。

                在程序代碼的第34行,有一行被注釋了的內(nèi)容//parent1::fun2();,之所以會(huì)注釋掉,是因?yàn)檫@樣的代碼是無法通過編譯的,因?yàn)樵趐arent1類里,fun2是一個(gè)“純虛函數(shù)”也就是說這個(gè)函數(shù)沒有代碼實(shí)體,在編譯的時(shí)候,鏈接器將無法找到fun2的目標(biāo)代碼從而報(bào)錯(cuò)。

                其實(shí)有了對(duì)虛函數(shù)的正確的認(rèn)識(shí),上面的多級(jí)繼承是很自然就能明白的。然而在多繼承的情況下,情況就有所不同了……

                多繼承下虛函數(shù)的使用

                假如一個(gè)類,它由多個(gè)父類繼承而來,而在不同的父類的繼承體系里,都存在虛函數(shù)的時(shí)候,這個(gè)類的對(duì)象布局又會(huì)是怎樣的?它又是怎樣定位虛函數(shù)的呢?

                讓我們看看下面的代碼:
             //Source filename: Win32Con.cpp
             #include <iostream>
             using namespace std;
            class parent1
             {
            public:
                virtual int fun1(){cout<<"parent1::fun1()"<<endl;return 0;};
            };
            class parent2
             {
            public:
                virtual int fun2(){cout<<"parent2::fun2()"<<endl;return 0;};
            };
            class child1:public parent1,public parent2
             {
            public:
                virtual int fun1()
                {
                    cout<<"child1::fun1()"<<endl;
                    return 0;
                }
                virtual int fun2()
                {
                    cout<<"child1::fun2()"<<endl;
                    return 0;
                }
            };
            void test_func1(parent1 *pp)
            {
                pp->fun1();
            }
            void test_func2(parent2 *pp)
            {
                pp->fun2();
            }
            int main(int argc, char* argv[])
            {
                child1 chobj;
                test_func1(&chobj);
                test_func2(&chobj);
                return 0;
            }
             


                這一次,我們有兩個(gè)父類,parent1和parent2,在parent1里定義了虛函數(shù)fun1,而在parent2里定義了虛函數(shù)fun2,然后我們有一個(gè)子類child1,在里面重新實(shí)現(xiàn)了fun1和 fun2兩個(gè)虛函數(shù)。然后我們編寫了test_func1函數(shù)來調(diào)用parent1類型對(duì)象的fun1函數(shù),編寫了test_func2函數(shù)調(diào)用parent2對(duì)象的fun2函數(shù)。在main函數(shù)里我們實(shí)例化了一個(gè)child1類型的對(duì)象chobj,然后分別傳給test_func1和test_func2去執(zhí)行。

                這段代碼的運(yùn)行結(jié)果非常簡(jiǎn)單就能看出來:

                child1::fun1()

                child1::fun2()

                但是,讓我們看看對(duì)象布局吧:
             class child1    size(8):
                    +---
                    | +--- (base class parent1)
             0      | | {vfptr}
                    | +---
                    | +--- (base class parent2)
             4      | | {vfptr}
                    | +---
                    +---

            child1::$vftable@parent1@:
                    | &child1_meta
                    |  0
             0      | &child1::fun1

            child1::$vftable@parent2@:
                    | -4
             0      | &child1::fun2

            child1::fun1 this adjustor: 0
            child1::fun2 this adjustor: 4

             


                注意到?jīng)]?在child1的對(duì)象布局里,出現(xiàn)了兩個(gè)vfptr指針!

                這兩個(gè)虛函數(shù)表指針分別繼承于parent1和parent2類,分別指向了不同的兩個(gè)虛函數(shù)表。

                問題來了,當(dāng)我們使用test_func1調(diào)用parent1類的fun1函數(shù)的時(shí)候,調(diào)用個(gè)過程還比較好理解,可以從傳入的地址參數(shù)取得繼承自parent1的vfptr,從而執(zhí)行正確的fun1函數(shù)代碼,但是當(dāng)我們調(diào)用test_func2函數(shù)的時(shí)候,為什么程序可以自動(dòng)取得來自parent2的vfptr呢,從而得出正確的fun2函數(shù)的地址呢?

                其實(shí),這個(gè)工作是編譯器自動(dòng)根據(jù)實(shí)例的類型完成的,在編譯階段就已經(jīng)確定了在調(diào)用test_func2的時(shí)候,傳入的this指針需要增加一定的偏移(在這里則是第一個(gè)vfptr所占用的大小,也就是4字節(jié))。

                我們可以看看main函數(shù)里這部分代碼的反匯編代碼:
               child1 chobj;
            00F5162E 8D 4D F4         lea         ecx,[chobj]
            00F51631 E8 F5 FB FF FF   call        child1::child1 (0F5122Bh)
                test_func1(&chobj);
            00F51636 8D 45 F4         lea         eax,[chobj]
            00F51639 50               push        eax
            00F5163A E8 6F FB FF FF   call        test_func1 (0F511AEh)
            00F5163F 83 C4 04         add         esp,4
                test_func2(&chobj);
            00F51642 8D 45 F4         lea         eax,[chobj]
            00F51645 85 C0            test        eax,eax
            00F51647 74 0E            je          main+47h (0F51657h)
            00F51649 8D 4D F4         lea         ecx,[chobj]
            00F5164C 83 C1 04         add         ecx,4
            00F5164F 89 8D 2C FF FF FF mov         dword ptr [ebp-0D4h],ecx
            00F51655 EB 0A            jmp         main+51h (0F51661h)
            00F51657 C7 85 2C FF FF FF 00 00 00 00 mov         dword ptr [ebp-0D4h],0
            00F51661 8B 95 2C FF FF FF mov         edx,dword ptr [ebp-0D4h]
            00F51667 52               push        edx
            00F51668 E8 F6 FA FF FF   call        test_func2 (0F51163h)
            00F5166D 83 C4 04         add         esp,4
                return 0;


                從第4行至第5行,執(zhí)行的是test_func1函數(shù),this指針指向 chobj (第2行l(wèi)ea ecx,[chobj]),但是調(diào)用test_func2函數(shù)的時(shí)候,this指針被增加了4(第14行)!于是,在test_func2執(zhí)行的時(shí)候,就可以從&chobj+4的地方獲得vfptr指針,從而根據(jù)parent2的對(duì)象布局得到了fun2的地址并執(zhí)行了。

                為了證實(shí)這點(diǎn),我們可以將代碼做如下的修改:
                 1:  int main(int argc, char* argv[])

                2:  {

                3:      child1 chobj;

                4:      test_func1(&chobj);

                5:      test_func2((parent2 *)(void *)&chobj);

                6:      return 0;

                7:  }

                8:
             


                請(qǐng)注意紅色部分的變化,在講chobj傳入給test_func2之前,先用(void *)強(qiáng)制轉(zhuǎn)換為無類型指針,再轉(zhuǎn)換為parent2 指針,這樣的轉(zhuǎn)換,顯然是可行的,因?yàn)閏hobj本身就是parent2的子類,然而,程序的執(zhí)行效果卻是:

                child1::fun1()

                child1::fun1()

                執(zhí)行test_func2函數(shù),調(diào)用的是parent2::fun2,但是居然執(zhí)行的是child1::fun1()函數(shù)!!!

                這中間發(fā)生了些什么呢?我們?cè)倏纯捶磪R編的代碼:
               child1 chobj;
            013D162E 8D 4D F4         lea         ecx,[chobj]
            013D1631 E8 F5 FB FF FF   call        child1::child1 (13D122Bh)
                test_func1(&chobj);
            013D1636 8D 45 F4         lea         eax,[chobj]
            013D1639 50               push        eax
            013D163A E8 6F FB FF FF   call        test_func1 (13D11AEh)
            013D163F 83 C4 04         add         esp,4
                test_func2((parent2*)(void *)&chobj);
            013D1642 8D 45 F4         lea         eax,[chobj]
            013D1645 50               push        eax
            013D1646 E8 18 FB FF FF   call        test_func2 (13D1163h)
            013D164B 83 C4 04         add         esp,4
                return 0;
             


                從調(diào)用test_func2的反匯編代碼可以看到,這一次ecx寄存器的值沒有做改變!所以在執(zhí)行test_func2的時(shí)候,將取得parent1對(duì)象布局里的vfptr,而這個(gè)vfptr所指的虛函數(shù)表里的第一項(xiàng)就是fun1,并且被填寫為child1::fun1的地址了。所以才出現(xiàn)了child::fun1的輸出內(nèi)容!顯然這里有一個(gè)隱藏的致命問題,加入parent1和parent2的第一個(gè)虛函數(shù)的參數(shù)列表不一致,這樣的調(diào)用顯然就會(huì)導(dǎo)致堆棧被破壞掉,程序99%會(huì)立即崩潰。之前的程序沒有崩潰并且成功輸出內(nèi)容,不過是因?yàn)閜arent1::fun1()和parent2::fun2()的參數(shù)列表一致的關(guān)系而已。

                所以,千萬不要在使用一個(gè)多繼承對(duì)象的時(shí)候,將其類型信息丟棄,編譯器還需要依靠正確的類型信息,在使用虛函數(shù)的時(shí)候來得到正確的匯編代碼!

             

             

            posted @ 2009-08-05 17:45 chaosuper 閱讀(182) | 評(píng)論 (0)編輯 收藏

            僅列出標(biāo)題
            共12頁: First 3 4 5 6 7 8 9 10 11 Last 
            国产成人99久久亚洲综合精品 | 亚洲成人精品久久| 亚洲国产精品人久久| 久久久久国产精品麻豆AR影院| 久久综合一区二区无码| 国产精品对白刺激久久久| 狠狠干狠狠久久| 久久久亚洲欧洲日产国码是AV| 久久99国产精品久久| 亚洲精品久久久www| 2020久久精品国产免费| 久久成人小视频| 91麻精品国产91久久久久| 超级碰碰碰碰97久久久久| 久久国产精品视频| 久久精品人成免费| 亚洲综合久久久| 久久精品国产色蜜蜜麻豆| 国产精品18久久久久久vr| 久久亚洲AV无码精品色午夜| 久久精品国产亚洲Aⅴ蜜臀色欲| 国产精品久久久福利| 亚洲va久久久噜噜噜久久狠狠| 久久影院亚洲一区| 久久夜色精品国产| 国产日韩欧美久久| 精品久久久久久久中文字幕| 91精品国产乱码久久久久久| 国产成人精品三上悠亚久久| 香蕉久久夜色精品国产2020| 亚洲精品乱码久久久久久蜜桃| 国产高清美女一级a毛片久久w| AV无码久久久久不卡网站下载| 无码超乳爆乳中文字幕久久 | 国产亚州精品女人久久久久久| 久久精品夜夜夜夜夜久久| 久久精品无码午夜福利理论片| 人妻无码αv中文字幕久久| 久久久久久久97| 久久久国产精品亚洲一区| 久久精品国产精品亚洲毛片 |