国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 常見問題 > 正文

你必須注意的11個(gè)C++要點(diǎn)

2019-12-21 02:48:09
字體:
供稿:網(wǎng)友
很顯然,它們對C++程序員來說是永久的好資料。我相信這一篇文章會使你收獲不小

你必須注意的11個(gè)C++要點(diǎn)

下面的這些要點(diǎn)是對所有的C++程序員都適用的。我之所以說它們是最重要的,是因?yàn)檫@些要點(diǎn)中提到的是你通常在C++書中或網(wǎng)站上無法找到的。如:指向成員的指針,這是許多資料中都不愿提到的地方,也是經(jīng)常出錯(cuò)的地方,甚至是對一些高級的C++程序員也是如此。

這里的要點(diǎn)不僅僅是解釋怎樣寫出更好的代碼,更多的是展現(xiàn)出語言規(guī)則里面的東西。很顯然,它們對C++程序員來說是永久的好資料。我相信這一篇文章會使你收獲不小。

首先,我把一些由不同層次的C++程序員經(jīng)常問的問題歸到一起。我驚奇的發(fā)現(xiàn)有很多是有經(jīng)驗(yàn)的程序員都還沒意識到 .h 符號是否還應(yīng)該出現(xiàn)在標(biāo)準(zhǔn)頭文件中。

要點(diǎn)1: <iostream.h> 還是 <iostream>?

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

因?yàn)檫@些實(shí)質(zhì)上的不同,你不能在一個(gè)程序中混淆使用這兩個(gè)庫。做為一種習(xí)慣,在新的代碼中一般使用<iostream>,但如果你處理的是過去編寫的代碼,為了繼承可以用繼續(xù)用<iostream.h>舊保持代碼的一致性。

要點(diǎn)2:用引用傳遞參數(shù)時(shí)應(yīng)注意的地方

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

void f(const int & i);

int main()

{

f(2); /* OK */

}

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

這個(gè)要點(diǎn)也適用于用戶定義的對象。你可以給臨時(shí)對象也加上引用如果是const類型:

struct A{};

void f(const A& a);

int main()

{

f(A()); // OK,傳遞的是一個(gè)臨時(shí)A的const引用

}

要點(diǎn)3:“逗號分離”表達(dá)形式

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

一個(gè)表達(dá)式由一個(gè)或多個(gè)其它表達(dá)式構(gòu)成,由逗號分開,如:

if(++x, --y, cin.good()) //三個(gè)表達(dá)式

這個(gè)if條件包含了三個(gè)由逗號分離的表達(dá)式。C++會計(jì)算每個(gè)表達(dá)式,但完整的“逗號分離”表達(dá)式的結(jié)果是最右邊表達(dá)式的值。因此,僅當(dāng)cin.good()返回true時(shí),if條件的值才是true。下面是另一個(gè)例子:

int j=10;

int i=0;

while( ++i, --j)

{

//直到j(luò)=0時(shí),循環(huán)結(jié)束,在循環(huán)時(shí),i不斷自加

}

要點(diǎn)4,使用全局對象的構(gòu)造函數(shù)在程序啟動前調(diào)用函數(shù)

有一些應(yīng)用程序需要在主程序啟動前調(diào)用其它函數(shù)。如:轉(zhuǎn)態(tài)過程函數(shù)、登記功能函數(shù)都是必須在實(shí)際程序運(yùn)行前被調(diào)用的。最簡單的辦法是通過一個(gè)全局對象的構(gòu)造函數(shù)來調(diào)用這些函數(shù)。因?yàn)槿謱ο蠖际窃谥鞒绦蜷_始前被構(gòu)造,這些函數(shù)都將會在main()之前返回結(jié)果。如:

class Logger

{

public:

Logger()

{

activate_log();//譯者注:在構(gòu)造函數(shù)中調(diào)用你需要先運(yùn)行的函數(shù)

}

};

Logger log; //一個(gè)全局實(shí)例

int main()

{

record * prec=read_log();//譯者注:讀取log文件數(shù)據(jù)

//.. 程序代碼

}

全局對象log在main()運(yùn)行之前被構(gòu)造,log調(diào)用了函數(shù)activate_log()。從而,當(dāng)main()開始執(zhí)行時(shí),它就可以從log文件中讀取數(shù)據(jù)。

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

要點(diǎn)5:避免使用復(fù)雜構(gòu)造的指向函數(shù)的指針

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

void (*p[10]) (void (*)());

P是一個(gè)“由10個(gè)指針構(gòu)成的指向一個(gè)返回void類型且指向另一個(gè)無返回和無運(yùn)算的函數(shù)的數(shù)組”。這個(gè)麻煩的語法真是讓人難以辨認(rèn),不是嗎?你其實(shí)可以簡單的通過typedef來聲明相當(dāng)于上面語句的函數(shù)。首先,使用typedef聲明“指向一個(gè)無返回和無運(yùn)算的函數(shù)的指針”:

typedef void (*pfv)();

接著,聲明“另一個(gè)指向無返回且使用pfv的函數(shù)指針”:

typedef void (*pf_taking_pfv) (pfv);

現(xiàn)在,聲明一個(gè)由10個(gè)上面這樣的指針構(gòu)成的數(shù)組:

pf_taking_pfv p[10];

與void (*p[10]) (void (*)())達(dá)到同樣效果。但這樣是不是更具有可讀性

了!

要點(diǎn)6:指向成員的指針

一個(gè)類有兩種基本的成員:函數(shù)成員和數(shù)據(jù)成員。同樣的,指向成員的指針也有兩種:指向函數(shù)成員的指針和指向數(shù)據(jù)成員的指針。后則其實(shí)并不常用,因?yàn)轭愐话闶遣缓泄矓?shù)據(jù)成員的,僅當(dāng)用在繼承用C寫的代碼時(shí)協(xié)調(diào)結(jié)構(gòu)(struct)和類(class)時(shí)才會用到。

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

指向數(shù)據(jù)成員的指針

盡管剛開始時(shí),指向成員的指針的語法會使你有一點(diǎn)點(diǎn)的迷惑,但你不久會發(fā)現(xiàn)它其實(shí)同普通的指針差不多,只不過是*號的前面多了::符號和類的名字,例:

定義一個(gè)指向int型的指針:

int * pi;

定義一個(gè)指向?yàn)閕nt型的類的數(shù)據(jù)成員:

int A::*pmi; //pmi是指向類A的一個(gè)int型的成員

你可以這樣初始化它:

class A

{

public:

int num;

int x;

};

int A::*pmi = & A::num;

上面的代碼是聲明一個(gè)指向類A的一個(gè)int型的num成員并將它初始化為這個(gè)num成員的地址.通過在pmi前面加上*你就可以使用和更改類A的num成員的值:

A a1, a2;

int n=a1.*pmi; //把a(bǔ)1.num賦值給n

a1.*pmi=5; // 把5賦值給a1.num

a2.*pmi=6; // 把6賦值給6a2.num

如果你定義了一個(gè)指向類A的指針,那么上面的操作你必須用 ->*操作符代

替:

A * pa=new A;

int n=pa->*pmi;

pa->*pmi=5;

指向函數(shù)成員的指針

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

class A

{

public:

int func ();

};

int (A::*pmf) ();

上面的定義也就是說pmf是一個(gè)指向類A的函數(shù)成員func()的指針.實(shí)際上,這個(gè)指針和一個(gè)普通的指向函數(shù)的指針沒什么不同,只是它包含了類的名字和::符號。你可以在在任何使用*pmf的地方調(diào)用這個(gè)函數(shù)

func():

pmf=&A::func;

A a;

(a.*pmf)(); //調(diào)用a.func()

如果你先定義了一個(gè)指向?qū)ο蟮闹羔槪敲瓷厦娴牟僮饕?>*代替:

A *pa=&a;

(pa->*pmf)(); //調(diào)用pa->func()

指向函數(shù)成員的指針要考慮多態(tài)性。所以,當(dāng)你通過指針調(diào)用一個(gè)虛函數(shù)成員時(shí),這個(gè)調(diào)用將會被動態(tài)回收。另一個(gè)需要注意的地方,你不能取一個(gè)類的構(gòu)造函數(shù)和析構(gòu)函數(shù)的地址。

要點(diǎn)7、避免產(chǎn)生內(nèi)存碎片

經(jīng)常會有這樣的情況:你的應(yīng)用程序每運(yùn)行一次時(shí)就因?yàn)槌绦蜃陨砣毕荻a(chǎn)生內(nèi)存漏洞而泄漏內(nèi)存,而你又在周期性地重復(fù)著你的程序,結(jié)果可想而知,它也會使系統(tǒng)崩潰。但怎樣做才能預(yù)防呢?

首先,盡量少使用動態(tài)內(nèi)存。在大多數(shù)情況下,你可能使用靜態(tài)或自動存儲或者是STL容器。第二,盡量分配大塊的內(nèi)存而不是一次只分配少量內(nèi)存。舉個(gè)例子:一次分配一個(gè)數(shù)組實(shí)例所需的內(nèi)存,而不是一次只分配一個(gè)數(shù)組元素的內(nèi)存。

要點(diǎn)8、是delete還是delete[]

在程序員中有個(gè)荒誕的說法:使用delete來代替delete[]刪除數(shù)組類型時(shí)是可以的!

舉個(gè)例子吧:

int *p=new int[10];

delete p; //錯(cuò)誤,應(yīng)該是:delete[] p

上面的程序是完全錯(cuò)誤的。事實(shí)上,在一個(gè)平臺上使用delete代替delete[]的應(yīng)用程序也許不會造成系統(tǒng)崩潰,但那純粹是運(yùn)氣。你不能保證你的應(yīng)用程序是不是會在另一個(gè)編譯器上編譯,在另一個(gè)平臺上運(yùn)行,所以還是請使用delete[]。

要點(diǎn)9、優(yōu)化成員的排列

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

struct A

{

bool a;

int b;

bool c;

}; //sizeof (A) == 12

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

struct B

{

bool a;

bool c;

int b;

}; // sizeof (B) == 8

這一次,編譯器只在成員c后插入了2個(gè)字節(jié)。因?yàn)閎占了4個(gè)字節(jié),所以就很自然地把它當(dāng)作一個(gè)字的形式排列,而a和c的大小1+1=2,再加上2個(gè)字節(jié)就剛好按兩個(gè)字的形式排列B。

要點(diǎn)10、為什么繼承一個(gè)沒有虛析構(gòu)函數(shù)的類是危險(xiǎn)的?

一個(gè)沒有虛析構(gòu)函數(shù)的類意味著不能做為一個(gè)基類。如std::string,std::complex, 和 std::vector 都是這樣的。為什么繼承一個(gè)沒有虛析構(gòu)函數(shù)的類是危險(xiǎn)的?當(dāng)你公有繼承創(chuàng)建一個(gè)從基類繼承的相關(guān)類時(shí),指向新類對象中的指針和引用實(shí)際上都指向了起源的對象。因?yàn)槲鰳?gòu)函數(shù)不是虛函數(shù),所以當(dāng)你delete一個(gè)這樣的類時(shí),C++就不會調(diào)用析構(gòu)函數(shù)鏈。舉個(gè)例子說明:

class A

{

public:

~A() // 不是虛函數(shù)

{

// ...

}

};

class B: public A //錯(cuò); A沒有虛析構(gòu)函數(shù)

{

public:

~B()

{

// ...

}

};

int main()

{

A * p = new B; //看上去是對的

delete p; //錯(cuò),B的析構(gòu)函沒有被調(diào)用

}

要點(diǎn)11、以友元類聲明嵌套的類

當(dāng)你以友元類聲明一個(gè)嵌套的類時(shí),把友元聲明放在嵌套類聲明的后面,而不前面。

class A

{

private:

int i;

public:

class B //嵌套類聲明在前

{

public:

B(A & a) { a.i=0;};

};

friend class B;//友元類聲明

};

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

本文轉(zhuǎn)自羅索實(shí)驗(yàn)室

發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 错那县| 巫溪县| 花垣县| 阳山县| 中江县| 巫溪县| 灵璧县| 三河市| 大足县| 利津县| 林口县| 赤壁市| 康乐县| SHOW| 承德市| 金山区| 孙吴县| 北安市| 噶尔县| 平遥县| 婺源县| 邳州市| 沙河市| 南部县| 临朐县| 右玉县| 九寨沟县| 乐山市| 彭山县| 策勒县| 韶山市| 邯郸县| 濮阳县| 汨罗市| 锦屏县| 大余县| 铜山县| 石棉县| 泊头市| 沂南县| 朝阳区|