午夜视频在线网站,日韩视频精品在线,中文字幕精品一区二区三区在线,在线播放精品,1024你懂我懂的旧版人,欧美日韩一级黄色片,一区二区三区在线观看视频

分享

高質(zhì)量C/C++編程(片段)

 bluecrystal 2006-09-04
高質(zhì)量C/C++編程(片段)

這段文字是我從林銳博士的<高質(zhì)量C/C++編程>節(jié)選出來的片段,使其便于快速閱讀

 

【規(guī)則1-2-1】為了防止頭文件被重復(fù)引用,應(yīng)當(dāng)用ifndef/define/endif結(jié)構(gòu)產(chǎn)生預(yù)處理塊。

l       【規(guī)則1-2-2】用 #include <filename.h> 格式來引用標(biāo)準(zhǔn)庫(kù)的頭文件(編譯器將從標(biāo)準(zhǔn)庫(kù)目錄開始搜索)。

l       【規(guī)則1-2-3】用 #include “filename.h” 格式來引用非標(biāo)準(zhǔn)庫(kù)的頭文件(編譯器將從用戶的工作目錄開始搜索)。

2     【建議1-2-1】頭文件中只存放“聲明”而不存放“定義

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

4.3.1 布爾變量與零值比較

l       【規(guī)則4-3-1】不可將布爾變量直接與TRUE、FALSE或者1、0進(jìn)行比較。

根據(jù)布爾類型的語義,零值為“假”(記為FALSE),任何非零值都是“真”(記為TRUE)。TRUE的值究竟是什么并沒有統(tǒng)一的標(biāo)準(zhǔn)。例如Visual C++ 將TRUE定義為1,而Visual Basic則將TRUE定義為-1。

假設(shè)布爾變量名字為flag,它與零值比較的標(biāo)準(zhǔn)if語句如下:

if (flag)   // 表示flag為真

if (!flag)   // 表示flag為假

其它的用法都屬于不良風(fēng)格,例如:

4.3.2 整型變量與零值比較

l       【規(guī)則4-3-2】應(yīng)當(dāng)將整型變量用“==”或“!=”直接與0比較。

  假設(shè)整型變量的名字為value,它與零值比較的標(biāo)準(zhǔn)if語句如下:

if (value == 0)

if (value != 0)


4.3.3 浮點(diǎn)變量與零值比較

l       【規(guī)則4-3-3】不可將浮點(diǎn)變量用“==”或“!=”與任何數(shù)字比較。

  千萬要留意,無論是float還是double類型的變量,都有精度限制。所以一定要避免將浮點(diǎn)變量用“==”或“!=”與數(shù)字比較,應(yīng)該設(shè)法轉(zhuǎn)化成“>=”或“<=”形式。

  假設(shè)浮點(diǎn)變量的名字為x,應(yīng)當(dāng)將  

if (x == 0.0)   // 隱含錯(cuò)誤的比較

轉(zhuǎn)化為

if ((x>=-EPSINON) && (x<=EPSINON))

其中EPSINON是允許的誤差(即精度)。


4.3.4 指針變量與零值比較

l       【規(guī)則4-3-4】應(yīng)當(dāng)將指針變量用“==”或“!=”與NULL比較。

  指針變量的零值是“空”(記為NULL)。盡管NULL的值與0相同,但是兩者意義不同。假設(shè)指針變量的名字為p,它與零值比較的標(biāo)準(zhǔn)if語句如下:

    if (p == NULL)   // p與NULL顯式比較,強(qiáng)調(diào)p是指針變量

    if (p != NULL)

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

循環(huán)語句的效率

--------------------------
const數(shù)據(jù)成員的初始化只能在類構(gòu)造函數(shù)的初始化表中進(jìn)行,例如

  class A

  {…

    A(int size);     // 構(gòu)造函數(shù)

    const int SIZE ;

  };

  A::A(int size) : SIZE(size)   // 構(gòu)造函數(shù)的初始化表

  {

    …

  }

  A a(100); // 對(duì)象 a 的SIZE值為100

  A b(200); // 對(duì)象 b 的SIZE值為200

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

規(guī)則6-1-3】如果參數(shù)是指針,且僅作輸入用,則應(yīng)在類型前加const,以防止該指針在函數(shù)體內(nèi)被意外修改。

例如:

void StringCopy(char *strDestination,const char *strSource);

       【規(guī)則6-1-4】如果輸入?yún)?shù)以值傳遞的方式傳遞對(duì)象,則宜改用“const &”方式來傳遞,這樣可以省去臨時(shí)對(duì)象的構(gòu)造和析構(gòu)過程,從而提高效率。


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


7.1內(nèi)存分配方式
內(nèi)存分配方式有三種:

(1)     從靜態(tài)存儲(chǔ)區(qū)域分配。內(nèi)存在程序編譯的時(shí)候就已經(jīng)分配好,這塊內(nèi)存在程序的整個(gè)運(yùn)行期間都存在。例如全局變量,static變量。

(2)     在棧上創(chuàng)建。在執(zhí)行函數(shù)時(shí),函數(shù)內(nèi)局部變量的存儲(chǔ)單元都可以在棧上創(chuàng)建,函數(shù)執(zhí)行結(jié)束時(shí)這些存儲(chǔ)單元自動(dòng)被釋放。棧內(nèi)存分配運(yùn)算內(nèi)置于處理器的指令集中,效率很高,但是分配的內(nèi)存容量有限。

(3)     從堆上分配,亦稱動(dòng)態(tài)內(nèi)存分配。程序在運(yùn)行的時(shí)候用malloc或new申請(qǐng)任意多少的內(nèi)存,程序員自己負(fù)責(zé)在何時(shí)用free或delete釋放內(nèi)存。動(dòng)態(tài)內(nèi)存的生存期由我們決定,使用非常靈活,但問題也最多。

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


u     內(nèi)存分配未成功,卻使用了它。

編程新手常犯這種錯(cuò)誤,因?yàn)樗麄儧]有意識(shí)到內(nèi)存分配會(huì)不成功。常用解決辦法是,在使用內(nèi)存之前檢查指針是否為NULL。如果指針p是函數(shù)的參數(shù),那么在函數(shù)的入口處用assert(p!=NULL)進(jìn)行檢查。如果是用malloc或new來申請(qǐng)內(nèi)存,應(yīng)該用if(p==NULL) 或if(p!=NULL)進(jìn)行防錯(cuò)處理。

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

C++/C程序中,指針和數(shù)組在不少地方可以相互替換著用,讓人產(chǎn)生一種錯(cuò)覺,以為兩者是等價(jià)的。

    數(shù)組要么在靜態(tài)存儲(chǔ)區(qū)被創(chuàng)建(如全局?jǐn)?shù)組),要么在棧上被創(chuàng)建。數(shù)組名對(duì)應(yīng)著(而不是指向)一塊內(nèi)存,其地址與容量在生命期內(nèi)保持不變,只有數(shù)組的內(nèi)容可以改變。

指針可以隨時(shí)指向任意類型的內(nèi)存塊,它的特征是“可變”,所以我們常用指針來操作動(dòng)態(tài)內(nèi)存。指針遠(yuǎn)比數(shù)組靈活,但也更危險(xiǎn)

示例7-3-1中,字符數(shù)組a的容量是6個(gè)字符,其內(nèi)容為hello\0。a的內(nèi)容可以改變,如a[0]= ‘X’。指針p指向常量字符串“world”(位于靜態(tài)存儲(chǔ)區(qū),內(nèi)容為world\0),常量字符串的內(nèi)容是不可以被修改的。從語法上看,編譯器并不覺得語句p[0]= ‘X’有什么不妥,但是該語句企圖修改常量字符串的內(nèi)容而導(dǎo)致運(yùn)行錯(cuò)誤。

7.3.2 內(nèi)容復(fù)制與比較

  不能對(duì)數(shù)組名進(jìn)行直接復(fù)制與比較。示例7-3-2中,若想把數(shù)組a的內(nèi)容復(fù)制給數(shù)組b,不能用語句 b = a ,否則將產(chǎn)生編譯錯(cuò)誤。應(yīng)該用標(biāo)準(zhǔn)庫(kù)函數(shù)strcpy進(jìn)行復(fù)制。同理,比較b和a的內(nèi)容是否相同,不能用if(b==a) 來判斷,應(yīng)該用標(biāo)準(zhǔn)庫(kù)函數(shù)strcmp進(jìn)行比較。

  語句p = a 并不能把a(bǔ)的內(nèi)容復(fù)制指針p,而是把a(bǔ)的地址賦給了p。要想復(fù)制a的內(nèi)容,可以先用庫(kù)函數(shù)malloc為p申請(qǐng)一塊容量為strlen(a)+1個(gè)字符的內(nèi)存,再用strcpy進(jìn)行字符串復(fù)制。同理,語句if(p==a) 比較的不是內(nèi)容而是地址,應(yīng)該用庫(kù)函數(shù)strcmp來比較。

 

  // 數(shù)組…

  char a[] = "hello";

  char b[10];

  strcpy(b, a);       // 不能用   b = a;

  if(strcmp(b, a) == 0)   // 不能用 if (b == a)

  // 指針…

  int len = strlen(a);

  char *p = (char *)malloc(sizeof(char)*(len+1));

  strcpy(p,a);         // 不要用 p = a;

  if(strcmp(p, a) == 0)   // 不要用 if (p == a)

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

針p指向a,但是sizeof(p)的值卻是4。這是因?yàn)閟izeof(p)得到的是一個(gè)指針變量的字節(jié)數(shù),相當(dāng)于sizeof(char*),而不是p所指的內(nèi)存容量

  void Func(char a[100])

  {

    cout<< sizeof(a) << endl;   // 4字節(jié)而不是100字節(jié)

}

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

毛病出在函數(shù)GetMemory中。編譯器總是要為函數(shù)的每個(gè)參數(shù)制作臨時(shí)副本,指針參數(shù)p的副本是 _p,編譯器使 _p = p。如果函數(shù)體內(nèi)的程序修改了_p的內(nèi)容,就導(dǎo)致參數(shù)p的內(nèi)容作相應(yīng)的修改。這就是指針可以用作輸出參數(shù)的原因。在本例中,_p申請(qǐng)了新的內(nèi)存,只是把_p所指的內(nèi)存地址改變了,但是p絲毫未變。所以函數(shù)GetMemory并不能輸出任何東西。事實(shí)上,每執(zhí)行一次GetMemory就會(huì)泄露一塊內(nèi)存,因?yàn)闆]有用free釋放內(nèi)存

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

用函數(shù)返回值來傳遞動(dòng)態(tài)內(nèi)存這種方法雖然好用,但是常常有人把return語句用錯(cuò)了。這里強(qiáng)調(diào)不要用return語句返回指向“棧內(nèi)存”的指針,因?yàn)樵搩?nèi)存在函數(shù)結(jié)束時(shí)自動(dòng)消亡,見示例7-4-4。

 

char *GetString(void)

{

  char p[] = "hello world";

  return p;   // 編譯器將提出警告

}

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

用調(diào)試器跟蹤示例7-5,發(fā)現(xiàn)指針p被free以后其地址仍然不變(非NULL),只是該地址對(duì)應(yīng)的內(nèi)存是垃圾,p成了“野指針”。如果此時(shí)不把p設(shè)置為NULL,會(huì)讓人誤以為p是個(gè)合法的指針。

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

alloc與free是C++/C語言的標(biāo)準(zhǔn)庫(kù)函數(shù),new/delete是C++的運(yùn)算符。它們都可用于申請(qǐng)動(dòng)態(tài)內(nèi)存和釋放內(nèi)存。
對(duì)于非內(nèi)部數(shù)據(jù)類型的對(duì)象而言,光用maloc/free無法滿足動(dòng)態(tài)對(duì)象的要求。對(duì)象在創(chuàng)建的同時(shí)要自動(dòng)執(zhí)行構(gòu)造函數(shù),對(duì)象在消亡之前要自動(dòng)執(zhí)行析構(gòu)函數(shù)。由于malloc/free是庫(kù)函數(shù)而不是運(yùn)算符,不在編譯器控制權(quán)限之內(nèi),不能夠把執(zhí)行構(gòu)造函數(shù)和析構(gòu)函數(shù)的任務(wù)強(qiáng)加于malloc/free。

 函數(shù)malloc的原型如下:
  void * malloc(size_t size);
 用malloc申請(qǐng)一塊長(zhǎng)度為length的整數(shù)類型的內(nèi)存,程序如下:
  int  *p = (int *) malloc(sizeof(int) * length);
我們應(yīng)當(dāng)把注意力集中在兩個(gè)要素上:“類型轉(zhuǎn)換”和“sizeof”。

u malloc返回值的類型是void *,所以在調(diào)用malloc時(shí)要顯式地進(jìn)行類型轉(zhuǎn)換,將void * 轉(zhuǎn)換成所需要的指針類型。
u malloc函數(shù)本身并不識(shí)別要申請(qǐng)的內(nèi)存是什么類型,它只關(guān)心內(nèi)存的總字節(jié)數(shù)。我們通常記不住int, float等數(shù)據(jù)類型的變量的確切字節(jié)數(shù)。例如int變量在16位系統(tǒng)下是2個(gè)字節(jié),在32位下是4個(gè)字節(jié);而float變量在16位系統(tǒng)下是4個(gè)字節(jié),在32位下也是4個(gè)字節(jié)。

free(p)能正確地釋放內(nèi)存。如果p是NULL指針,那么free對(duì)p無論操作多少次都不會(huì)出問題。如果p不是NULL指針,那么free對(duì)p連續(xù)操作兩次就會(huì)導(dǎo)致程序運(yùn)行錯(cuò)誤。

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

 運(yùn)算符new使用起來要比函數(shù)malloc簡(jiǎn)單得多,例如:
int  *p1 = (int *)malloc(sizeof(int) * length);
int  *p2 = new int[length];
這是因?yàn)閚ew內(nèi)置了sizeof、類型轉(zhuǎn)換和類型安全檢查功能。對(duì)于非內(nèi)部數(shù)據(jù)類型的對(duì)象而言,new在創(chuàng)建動(dòng)態(tài)對(duì)象的同時(shí)完成了初始化工作。如果對(duì)象有多個(gè)構(gòu)造函數(shù),那么new的語句也可以有多種形式。例如

在用delete釋放對(duì)象數(shù)組時(shí),留意不要丟了符號(hào)‘[]’。例如
 delete []objects; // 正確的用法
delete objects; // 錯(cuò)誤的用法
后者相當(dāng)于delete objects[0],漏掉了另外99個(gè)對(duì)象。

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

如果C++程序要調(diào)用已經(jīng)被編譯后的C函數(shù),該怎么辦?
假設(shè)某個(gè)C函數(shù)的聲明如下:
void foo(int x, int y);
該函數(shù)被C編譯器編譯后在庫(kù)中的名字為_foo,而C++編譯器則會(huì)產(chǎn)生像_foo_int_int之類的名字用來支持函數(shù)重載和類型安全連接。由于編譯后的名字不同,C++程序不能直接調(diào)用C函數(shù)。C++提供了一個(gè)C連接交換指定符號(hào)extern“C”來解決這個(gè)問題。例如:
extern “C”
{
   void foo(int x, int y);
   … // 其它函數(shù)
}
或者寫成
extern “C”
{
   #include “myheader.h”
   … // 其它C頭文件
}
這就告訴C++編譯譯器,函數(shù)foo是個(gè)C連接,應(yīng)該到庫(kù)中找名字_foo而不是找_foo_int_int。C++編譯器開發(fā)商已經(jīng)對(duì)C標(biāo)準(zhǔn)庫(kù)的頭文件作了extern“C”處理,所以我們可以用#include 直接引用這些頭文件。

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

對(duì)于任意一個(gè)類A,如果不想編寫上述函數(shù),C++編譯器將自動(dòng)為A產(chǎn)生四個(gè)缺省的函數(shù),如
 A(void);     // 缺省的無參數(shù)構(gòu)造函數(shù)
 A(const A &a);    // 缺省的拷貝構(gòu)造函數(shù)
 ~A(void);     // 缺省的析構(gòu)函數(shù)
 A & operate =(const A &a); // 缺省的賦值函數(shù)

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

構(gòu)造從類層次的最根處開始,在每一層中,首先調(diào)用基類的構(gòu)造函數(shù),然后調(diào)用成員對(duì)象的構(gòu)造函數(shù)。析構(gòu)則嚴(yán)格按照與構(gòu)造相反的次序執(zhí)行,該次序是唯一的,否則編譯器將無法自動(dòng)執(zhí)行析構(gòu)過程。
一個(gè)有趣的現(xiàn)象是,成員對(duì)象初始化的次序完全不受它們?cè)诔跏蓟碇写涡虻挠绊?,只由成員對(duì)象在類中聲明的次序決定。這是因?yàn)轭惖穆暶魇俏ㄒ坏?,而類的?gòu)造函數(shù)可以有多個(gè),因此會(huì)有多個(gè)不同次序的初始化表。如果成員對(duì)象按照初始化表的次序進(jìn)行構(gòu)造,這將導(dǎo)致析構(gòu)函數(shù)無法得到唯一的逆序

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

u 如果輸入?yún)?shù)采用“指針傳遞”,那么加const修飾可以防止意外地改動(dòng)該指針,起到保護(hù)作用。
例如StringCopy函數(shù):
  void StringCopy(char *strDestination, const char *strSource);

對(duì)于非內(nèi)部數(shù)據(jù)類型的輸入?yún)?shù),應(yīng)該將“值傳遞”的方式改為“const引用傳遞”,目的是提高效率。例如將void Func(A a) 改為void Func(const A &a)。
對(duì)于內(nèi)部數(shù)據(jù)類型的輸入?yún)?shù),不要將“值傳遞”的方式改為“const引用傳遞”。否則既達(dá)不到提高效率的目的,又降低了函數(shù)的可理解性。例如void Func(int x) 不應(yīng)該改為void Func(const int &x)。

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

u 如果給以“指針傳遞”方式的函數(shù)返回值加const修飾,那么函數(shù)返回值(即指針)的內(nèi)容不能被修改,該返回值只能被賦給加const修飾的同類型指針。
例如函數(shù)
  const char * GetString(void);
如下語句將出現(xiàn)編譯錯(cuò)誤:
  char *str = GetString();
正確的用法是
  const char *str = GetString();
u 如果函數(shù)返回值采用“值傳遞方式”,由于函數(shù)會(huì)把返回值復(fù)制到外部臨時(shí)的存儲(chǔ)單元中,加const修飾沒有任何價(jià)值。
 例如不要把函數(shù)int GetInt(void) 寫成const int GetInt(void)。
 同理不要把函數(shù)A GetA(void) 寫成const A GetA(void),其中A為用戶自定義的數(shù)據(jù)類型

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

 任何不會(huì)修改數(shù)據(jù)成員的函數(shù)都應(yīng)該聲明為const類型。如果在編寫const成員函數(shù)時(shí),不慎修改了數(shù)據(jù)成員,或者調(diào)用了其它非const成員函數(shù),編譯器將指出錯(cuò)誤,這無疑會(huì)提高程序的健壯性。
以下程序中,類stack的成員函數(shù)GetCount僅用于計(jì)數(shù),從邏輯上講GetCount應(yīng)當(dāng)為const函數(shù)。編譯器將指出GetCount函數(shù)中的錯(cuò)誤。
 class Stack
{
   public:
  void  Push(int elem);
  int  Pop(void);
  int  GetCount(void)  const; // const成員函數(shù)
   private:
  int  m_num;
  int  m_data[100];
};

 int Stack::GetCount(void)  const
{
  ++ m_num; // 編譯錯(cuò)誤,企圖修改數(shù)據(jù)成員m_num
 Pop();  // 編譯錯(cuò)誤,企圖調(diào)用非const函數(shù)
 return m_num;
 }
 const成員函數(shù)的聲明看起來怪怪的:const關(guān)鍵字只能放在函數(shù)聲明的尾部,大概是因?yàn)槠渌胤蕉家呀?jīng)被占用了。

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

#ifndef GRAPHICS_H // 防止graphics.h被重復(fù)定義
#define GRAPHICS_H
#include <math.h>// 引用標(biāo)準(zhǔn)庫(kù)的頭文件…
#include “myheader.h” // 引用非標(biāo)準(zhǔn)庫(kù)的頭文件…
void Function1(…); // 全局函數(shù)聲明…
class Box    // 類結(jié)構(gòu)聲明{…};
#endif

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多