c++編程要點

來源:互聯網
上載者:User
要點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;//友元類聲明
};
 
  如果你把友元類聲明放在聲明嵌套類的前面,編譯器將拋棄友元類後的其它聲
明。  

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.