• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
  • c++ 的11個要點

    發表于:2007-05-26來源:作者:點擊數: 標簽:
    容易混淆的東東 下面的這些要點是對所有的C++ 程序員 都適用的。我之所以說它們是最重要 的,是因為這些要點中提到的是你通常在C++書中或網站上無法找到的。如:指向 成員的指針,這是許多資料中都不愿提到的地方,也是經常出錯的地方,甚至是對 一些高級的C
    容易混淆的東東

    下面的這些要點是對所有的C++程序員都適用的。我之所以說它們是最重要 
    的,是因為這些要點中提到的是你通常在C++書中或網站上無法找到的。如:指向
    成員的指針,這是許多資料中都不愿提到的地方,也是經常出錯的地方,甚至是對
    一些高級的C++程序員也是如此。
      這里的要點不僅僅是解釋怎樣寫出更好的代碼,更多的是展現出語言規則里面
    的東西。很顯然,它們對C++程序員來說是永久的好資料。我相信這一篇文章會使
    你收獲不小。

      首先,我把一些由不同層次的C++程序員經常問的問題歸到一起。我驚奇的發
    現有很多是有經驗的程序員都還沒意識到 .h 符號是否還應該出現在標準頭文件
    中。


    要點1: <iostream.h> 還是 <iostream>?

      很多C++程序員還在使用<iostream.h>而不是用更新的標準的<iostream>庫。
    這兩者都有什么不同呢?首先,5年前我們就開始反對把.h符號繼續用在標準的頭
    文件中。繼續使用過時的規則可不是個好的方法。從功能性的角度來講,
    <iostream>包含了一系列模板化的I/O類,相反地<iostream.h>只僅僅是支持字符
    流。另外,輸入輸出流的C++標準規范接口在一些微妙的細節上都已改進,因此,
    <iostream>和<iostream.h>在接口和執行上都是不同的。最后,<iostream>的各組
    成都是以STL的形式聲明的,然而<iostream.h>的各組成都是聲明成全局型的。

      因為這些實質上的不同,你不能在一個程序中混淆使用這兩個庫。做為一種習
    慣,在新的代碼中一般使用<iostream>,但如果你處理的是過去編寫的代碼,為了
    繼承可以用繼續用<iostream.h>舊保持代碼的一致性?! ?


    要點2:用引用傳遞參數時應注意的地方

      在用引用傳遞參數時,最好把引用聲明為const類型。這樣做的好處是:告訴
    程序不能修改這個參數。在下面的這個例子中函數f()就是傳遞的引用:

    void f(const int & i);
    int main()
    {
     f(2); /* OK */
    }

      這個程序傳遞一個參數2給f()。在運行時,C++創建一個值為2的int類型的臨
    時變量,并傳遞它的引用給f().這個臨時變量和它的引用從f()被調用開始被創建
    并存在直到函數返回。返回時,就被馬上刪除。注意,如果我們不在引用前加上
    const限定詞,則函數f()可能會更改它參數的值,更可能會使程序產生意想不到的
    行為。所以,別忘了const。

      這個要點也適用于用戶定義的對象。你可以給臨時對象也加上引用如果是
    const類型:

    struct A{};
    void f(const A& a);
    int main()
    {
     f(A()); // OK,傳遞的是一個臨時A的const引用
    }


    要點3:“逗號分離”表達形式

     “逗號分離”表達形式是從C繼承來的,使用在for-和while-循環中。當然,這
    條語法規則被認為是不直觀的。首先,我們來看看什么是“逗號分離”表達形式。

      一個表達式由一個或多個其它表達式構成,由逗號分開,如:

     if(++x, --y, cin.good()) //三個表達式
      這個if條件包含了三個由逗號分離的表達式。C++會計算每個表達式,但完整
    的“逗號分離”表達式的結果是最右邊表達式的值。因此,僅當cin.good()返回
    true時,if條件的值才是true。下面是另一個例子:
    int j=10;
    int i=0;
    while( ++i, --j)
    {
     //直到j=0時,循環結束,在循環時,i不斷自加
    }

    要點4,使用全局對象的構造函數在程序啟動前調用函數

      有一些應用程序需要在主程序啟動前調用其它函數。如:轉態過程函數、登記
    功能函數都是必須在實際程序運行前被調用的。最簡單的辦法是通過一個全局對象
    的構造函數來調用這些函數。因為全局對象都是在主程序開始前被構造,這些函數
    都將會在main()之前返回結果。如:
    class Logger
    {

     public:
     Logger()
      {
       activate_log();//譯者注:在構造函數中調用你需要先運行的函數
      }
    };
    Logger log; //一個全局實例

    int main()
    {
     record * prec=read_log();//譯者注:讀取log文件數據
     //.. 程序代碼
    }


      全局對象log在main()運行之前被構造,log調用了函數activate_log()。從
    而,當main()開始執行時,它就可以從log文件中讀取數據。


      毫無疑問地,在C++編程中內存管理是最復雜和最容易出現bug的地方。直接訪
    問原始內存、動態分配存儲和最大限度的發揮C++指令效率,都使你必須盡力避免

    有關內存的bug。
      
    要點5:避免使用復雜構造的指向函數的指針

      指向函數的指針是C++中可讀性最差的語法之一。你能告訴我下面語句的意思
    嗎?

    void (*p[10]) (void (*)());
      P是一個“由10個指針構成的指向一個返回void類型且指向另一個無返回和無
    運算的函數的數組”。這個麻煩的語法真是讓人難以辨認,不是嗎?你其實可以簡
    單的通過typedef來聲明相當于上面語句的函數。首先,使用typedef聲明“指向一
    個無返回和無運算的函數的指針”:
    typedef void (*pfv)();
      接著,聲明“另一個指向無返回且使用pfv的函數指針”:
    typedef void (*pf_taking_pfv) (pfv);
      現在,聲明一個由10個上面這樣的指針構成的數組:
    pf_taking_pfv p[10];
      與void (*p[10]) (void (*)())達到同樣效果。但這樣是不是更具有可讀性
    了!

    要點6:指向成員的指針

      一個類有兩種基本的成員:函數成員和數據成員。同樣的,指向成員的指針也
    有兩種:指向函數成員的指針和指向數據成員的指針。后則其實并不常用,因為類
    一般是不含有公共數據成員的,僅當用在繼承用C寫的代碼時協調結構(struct)和
    類(class)時才會用到。

      指向成員的指針是C++語法中最難以理解的構造之一,但是這也是一個C++最強
    大的特性。它可以讓你調用一個類的函數成員而不必知道這個函數的名字。這一個
    非常敏捷的調用工具。同樣的,你也可以通過使用指向數據成員的指針來檢查并改
    變這個數據而不必知道它的成員名字。

      指向數據成員的指針

      盡管剛開始時,指向成員的指針的語法會使你有一點點的迷惑,但你不久會發
    現它其實同普通的指針差不多,只不過是*號的前面多了::符號和類的名字,例:
    定義一個指向int型的指針:


    int * pi;
      定義一個指向為int型的類的數據成員:
    int A::*pmi; //pmi是指向類A的一個int型的成員
      你可以這樣初始化它:
    class A
    {
     public:
     int num;
     int x;
    };
    int A::*pmi = & A::num;
      上面的代碼是聲明一個指向類A的一個int型的num成員并將它初始化為這個num
    成員的地址.通過在pmi前面加上*你就可以使用和更改類A的num成員的值:
    A a1, a2;
    int n=a1.*pmi; //把a1.num賦值給n
    a1.*pmi=5; // 把5賦值給a1.num
    a2.*pmi=6; // 把6賦值給6a2.num

      如果你定義了一個指向類A的指針,那么上面的操作你必須用 ->*操作符代
    替:
    A * pa=new A;
    int n=pa->*pmi;
    pa->*pmi=5;

      指向函數成員的指針

      它由函數成員所返回的數據類型構成,類名后跟上::符號、指針名和函數的參
    數列表。舉個例子:一個指向類A的函數成員(該函數返回int類型)的指針:

    class A
    {
     public:
     int func ();
    };
    int (A::*pmf) ();

      上面的定義也就是說pmf是一個指向類A的函數成員func()的指針.實際上,這
    個指針和一個普通的指向函數的指針沒什么不同,只是它包含了類的名字和::符
    號。你可以在在任何使用*pmf的地方調用這個函數
    func():
    pmf=&A::func;
    A a;
    (a.*pmf)(); //調用a.func()
      如果你先定義了一個指向對象的指針,那么上面的操作要用->*代替:
    A *pa=&a;
    (pa->*pmf)(); //調用pa->func()
      指向函數成員的指針要考慮多態性。所以,當你通過指針調用一個虛函數成員
    時,這個調用將會被動態回收。另一個需要注意的地方,你不能取一個類的構造函
    數和析構函數的地址。

    要點7、避免產生內存碎片


      經常會有這樣的情況:你的應用程序每運行一次時就因為程序自身缺陷而產生
    內存漏洞而泄漏內存,而你又在周期性地重復著你的程序,結果可想而知,它也會
    使系統崩潰。但怎樣做才能預防呢?首先,盡量少使用動態內存。在大多數情況
    下,你可能使用靜態或自動存儲或者是STL容器。第二,盡量分配大塊的內存而不
    是一次只分配少量內存。舉個例子:一次分配一個數組實例所需的內存,而不是一
    次只分配一個數組元素的內存。

    要點8、是delete還是delete[]

      在程序員中有個荒誕的說法:使用delete來代替delete[]刪除數組類型時是可
    以的!
      舉個例子吧:

     int *p=new int[10];
     delete p; //錯誤,應該是:delete[] p
      上面的程序是完全錯誤的。事實上,在一個平臺上使用delete代替delete[]的
    應用程序也許不會造成系統崩潰,但那純粹是運氣。你不能保證你的應用程序是不
    是會在另一個編譯器上編譯,在另一個平臺上運行,所以還是請使用delete[]。

    要點9、優化成員的排列

      一個類的大小可以被下面的方式改變:

    struct A

    {
     bool a;
     int b;
     bool c;
    }; //sizeof (A) == 12

      在我的電腦上sizeof (A) 等于12。這個結果可能會讓你吃驚,因為A的成員總
    數是6個字節:1+4+1個字節。那另6字節是哪兒來的?編譯器在每個bool成員后面
    都插入了3個填充字節以保證每個成員都是按4字節排列,以便分界。你可以減少A
    的大小,通過以下方式:

    struct B
    {
     bool a;
     bool c;
     int b;
    }; // sizeof (B) == 8

      這一次,編譯器只在成員c后插入了2個字節。因為b占了4個字節,所以就很自
    然地把它當作一個字的形式排列,而a和c的大小1+1=2,再加上2個字節就剛好按兩
    個字的形式排列B。

    要點10、為什么繼承一個沒有虛析構函數的類是危險的?

      一個沒有虛析構函數的類意味著不能做為一個基類。如std::string,
    std::complex, 和 std::vector 都是這樣的。為什么繼承一個沒有虛析構函數的
    類是危險的?當你公有繼承創建一個從基類繼承的相關類時,指向新類對象中的指
    針和引用實際上都指向了起源的對象。因為析構函數不是虛函數,所以當你delete
    一個這樣的類時,C++就不會調用析構函數鏈。舉個例子說明:

    class A
    {
     public:
     ~A() // 不是虛函數
     {
     // ...
     }
    };
    class B: public A //錯; A沒有虛析構函數
    {
     public:
     ~B()
     {
     // ...
     }
    };

    int main()
    {
     A * p = new B; //看上去是對的
     delete p; //錯,B的析構函沒有被調用
    }




    要點11、以友元類聲明嵌套的類

      當你以友元類聲明一個嵌套的類時,把友元聲明放在嵌套類聲明的后面,而不
    前面。

    class A
    {
     private:
     int i;
     public:
     class B //嵌套類聲明在前
     {
      public:
      B(A & a) { a.i=0;};
     };
     friend class B;//友元類聲明
    };

      如果你把友元類聲明放在聲明嵌套類的前面,編譯器將拋棄友元類后的其它聲
    明。

    原文轉自:http://www.kjueaiud.com

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>