effectiveC 記憶體管理 學習筆記

2022-02-21 08:30:10 字數 3920 閱讀 2658

1.盡量使用初始化列表而不要再建構函式裡賦值,初始化順序和宣告的順序一致,一些型別如const,引用等,必須使用初始化。對於非內部資料型別成員物件應當採用初始化表,以獲取更高的效率。

example:

b::b(const a& a):m_a(a){}只呼叫了類a的拷貝建構函式

2.基類都使用虛析構函式,這樣才能在使用多型時,準確的析構派生類

3.operator>>和operator《決不能是成員函式。如果f是operator>>或operator<<,讓f成為非成員函式。如果f還需要訪問c的非公有成員,讓f成為c的友元函式。在類的內部,它可以用於靜態和非靜態成員。

4.盡可能使用const.const關鍵字實在是神通廣大。在類的外面,它可以用於全域性或名字空間常量,以及靜態物件(某一檔案或程式塊範圍內的區域性物件)。在類的內部,它可以用於靜態和非靜態成員.

對指標來說,可以指定指標本身為const,也可以指定指標所指的資料為const,或二者同時指定為const,還有,兩者都不指定為const:

char *p = "hello"; // 非const指標,

// 非const資料

const char *p = "hello"; // 非const指標,

// const資料

char * const p = "hello"; // const指標,

// 非const資料

const char * const p = "hello"; // const指標,

// const資料

5.盡量用「傳引用」而不用「傳值」

「通過值來傳遞乙個物件」的具體含義是由這個物件的類的拷貝建構函式定義的。這使得傳值成為一種非常昂貴的操作。

為避免這種潛在的昂貴的開銷,就不要通過值來傳遞物件,而要通過引用:

const student& returnstudent(const student& s)

這會非常高效:沒有建構函式或析構函式被呼叫,因為沒有新的物件被建立。

6.返回值:對於賦值函式,應當用「引用傳遞」的方式返回物件,對於相加函式應當用值傳遞,因為引用物件在函式結束時被銷毀。

例如:

class

string

//string的賦值函式operate = 的實現如下:

string & string::operate=(const string &other)

對於賦值函式,應當用「引用傳遞」的方式返回string物件。如果用「值傳遞」的方式,雖然功能仍然正確,但由於return語句要把 *this拷貝到儲存返回值的外部儲存單元之中,增加了不必要的開銷,降低了賦值函式的效率。例如:

string a,b,c;

… a = b; // 如果用「值傳遞」,將產生一次 *this 拷貝

a = b= c; // 如果用「值傳遞」,將產生兩次 *this 拷貝

對於相加函式,應當用「值傳遞」的方式返回string物件。如果改用「引用傳遞」,那麼函式返回值是乙個指向區域性物件temp的「引用」。由於temp是在棧上申請的變數,函式執行完畢後被銷毀,將導致返回的「引用」無效。

//

string的相加函式operate + 的實現如下:

string operate+(const string &s1, const string &s2)

如果函式返回值是乙個物件,要考慮return語句的效率。例如

return string(s1 + s2);

這是臨時物件的語法,表示「建立乙個臨時物件並返回它」。不要以為它與「先建立乙個區域性物件temp並返回它的結果」是等價的,如

string temp(s1 + s2);

return temp;

實質不然,上述**將發生三件事。首先,temp物件被建立,同時完成初始化;然後拷貝建構函式把temp拷貝到儲存返回值的外部儲存單元中;最後,temp在函式結束時被銷毀(呼叫析構函式)。然而「建立乙個臨時物件並返回它」的過程是不同的,編譯器直接把臨時物件建立並初始化在外部儲存單元中,省去了拷貝和析構的化費,提高了效率。

7.劃分全域性名字空間的好處

例如,假設library1.h定義了一些常量,其中包括:

const double lib_version = 1.204;

類似的,library2.h也定義了:

const int lib_version = 3;

很顯然,如果某個程式想同時包含library1.h和library2.h就會有問題。

要這麼做:

namespace

sdm ;

handle&gethandle();

}

使用者於是可以通過三種方法來訪問這一名字空間裡的符號:將名字空間中的所有符號全部引入到某一使用者空間;將部分符號引入到某一使用者空間;或通過修飾符顯式地一次性使用某個符號:

void

f1()

void

f2()

void

f3()

8.將檔案間的編譯依賴性降至最低

假設某一天你開啟自己的c++程式**,然後對某個類的實現做了小小的改動。提醒你,改動的不是介面,而是類的實現,也就是說,只是細節部分。然後你準備重新生成程式,心想,編譯和鏈結應該只會花幾秒種。畢竟,只是改動了乙個類嘛!於是你點選了一下"rebuild",或輸入make(或其它類似命令)。然而,等待你的是驚愕,接著是痛苦。因為你發現,整個世界都在被重新編譯、重新鏈結!

在name.h中定義

class name{};

在person.h中:

#include"

name.h

"class

person;

在其他檔案中:

class man:public person;

class woman:public person;

這時候person檔案和name.h之間建立了編譯依賴關係,如果name改變了它的實現,或者name依賴的類改變了實現,包含person類的檔案以及任何使用了person類的檔案就必須重新編譯。這時person,man,和woman的檔案都要重新編譯。

為了實現定義與實現細節分開,我們可以這樣定義person:

class name;//

提前宣告

class

person;

如果這樣做可行,person的使用者就不需要重新編譯,只可惜用起來才知道:

int

main()

當看到x的定義時,編譯器知道必須為它分配乙個int大小的記憶體。這沒問題,每個編譯器都知道乙個int有多大。然而,當看到p的定義時,編譯器雖然知道必須為它分配乙個person大小的記憶體,但怎麼知道乙個person物件有多大呢?

對應於上面的**,或許可以這樣做:

int

main()

下面具體介紹怎麼採用這一技術來實現person介面和實現的分離:

class name;//

提前宣告

class

man;

class

woman;

class

person;

//在person.cpp中:

#include

"person.h

"#include

"man.h

"person.cpp::person(

const name&_pname)

string person::namestr()const

這樣,person作為介面與實現完全分離。編譯時不對name的改變產生依賴

記憶體管理學習筆記

1.棧 堆和靜態區 靜態區 儲存自動全域性變數和static變數 包括全域性靜態變數和區域性靜態變數 靜態區的內容在整個程式的生命週期內都存在。棧 儲存區域性變數。棧上的內容只在函式的範圍內存在,當函式執行結束,這些內容也會自動被銷毀。其特點是效率高,但是空間大小有限。堆 由malloc系列函式或n...

cocos記憶體管理學習

口訣 1.誰建立,誰釋放。通過new或者clone建立的物件,必須呼叫release或者autorelease。2.new,clone以外的方法建立的物件都被宣告了autorelease。3.誰retain,誰release。無論這個物件是如何生成的,只要呼叫了retain就要呼叫release。正...

object c 記憶體管理學習筆記

nsautoreleasepool pool nsautoreleasepool alloc init pool drain 這個函式可以把autoreleasepool裡的物件釋放 在for迴圈中每次都釋放記憶體池的示例 nsautoreleasepool temppool for i 0 i a...