c 學習筆記 類和動態記憶體分配與特殊成員函式

2021-07-04 16:50:55 字數 4233 閱讀 6018

上乙個例子中是仿string類str使用的是char陣列類儲存,但是這樣有大小限制(100),能不能讓它動態的進行調整大小呢?

已定義的char陣列行不通,但是讓乙個指標指向一塊規定大小的記憶體是可以的,於是我可以使用記憶體分配來完成任意大小的str類

這種方法叫動態記憶體分配

型別指標=new 型別名;

char *pstr=new char[n];

strcpy(pstr,"c++");

為pstr指標分配n個char大小的記憶體來儲存資料,然後用strcpy把「c++」複製到pstr指標指向的位址。

new關鍵字會開闢乙個大小為n個char型別大小的空間,然後讓指標指向它。

於是可以修改一下類str,使得更好用:

class str

str(const char *pstr)

str operator+(const str &s)

friend str operator+(const char *pstr, const str &s)

str &operator=(const str &s)

str &operator=(const char *pstr) //當str物件=字串指標時的特別情況

friend ostream &operator<<(ostream &os, const str &s)

friend istream &operator>>(istream &is, str &s) //過載輸入運算子,能夠使用cin>>str物件;

operator int()

~str() //使用delete釋放str指向的記憶體

};

建構函式會為物件的str指標分配記憶體,大小是形參pstr指標指向的內容的大小:strlen(pstr);該函式返回乙個int值,函式會計算pstr的大小,最後把n也設定為pstr的大小,因為n是用來記錄str物件內字串大小用的。

先來看看析構函式,在物件生命結束時會自動呼叫析構函式,如果使用預設的析構函式進行清理,那樣結果會如何?

預設析構函式會把物件的資料成員進行清理,比如int n會被清理、char *str也會被清理,但是有個嚴重的問題,str指向的記憶體怎麼辦?

於是可以在建構函式裡加入delete來清理動態分配的記憶體,析構函式會先執行函式體的內容,再進行清理工作,所以會先執行deletestr;再清理int n;和char *str;

關於過載運算子">>",該方法接受乙個istream和str物件作為引數,並返回乙個istream物件,先來看看是怎麼工作的:

str s4;

cin>>s4;

使用者輸入後並按下回車鍵確認時,將呼叫operator(cin,s4);,而使用者輸入的內容將被儲存在輸入流中,在》函式裡is.get()就相當於cin.get(),能夠讀取到輸入流的內容,如果使用者輸入錯誤,is物件裡將設定錯誤資訊,可以用if判斷,現在可以先不用了解它是如何工作的,只需要知道當輸入的內容不符合型別時,而且無法通過轉換識別,那麼cin將設定錯誤資訊,所以可以用if(is)判斷輸入流是否正確。

然後使用乙個迴圈來清空輸入流,當獲取的字元不是'\n\'和is獲取沒有出錯時繼續獲取。

在類裡有一些特殊成員函式,當使用者沒有為他們定義時將自動提供:

預設建構函式、預設析構函式、複製建構函式、賦值運算子、位址運算子。

如果沒有提供任何建構函式,將自動建立預設建構函式

類名(){};

建構函式:類名(引數列表);

例如有乙個a類,但是沒有提供任何建構函式,c++將自動提供a(){}建構函式,它將a類物件裡的資料成員初始化。

當顯式地提供了建構函式,比如a(int n);,c++將不會定義預設建構函式,這種情況下定義物件時不進行初始化賦值就會發生錯誤:

a a1;//編譯器將找不到a(){}建構函式,所以如果提供了建構函式則把預設建構函式也提供。

物件過期時,例如在fun()函式裡定義的a a1作為臨時變數,在執行完函式後a1就會自動呼叫a1物件的析構函式。

~類名();

如果使用者沒有顯式地提供析構函式,c++將自動提供預設的析構函式:例如~a(){};

它會對物件裡的資料成員進行清理,但是如果存在資料成員指標動態分配記憶體,則只會清理指標變數,而不會清理指向的記憶體,這時候就需要在析構函式裡使用delete釋放指向的記憶體。析構函式的執行順序是先執行析構函式函式體內的內容,然後清理物件裡的資料成員。

預設的複製建構函式是c++自動提供的,比如說類a,它的預設複製建構函式就是a(const a &)。

類名(const 類名 &形參名);

當沒有顯式地提供複製建構函式,c++會提供乙個預設的建構函式,他的功能是把非靜態的資料成員逐個複製到新物件中對應的資料成員:

假如類a:

class a

;}

a a1(3,2.5);

a a2(a1);//呼叫複製建構函式

a a2(a1)這個表示式將呼叫預設的複製建構函式把物件a1中,把a1中的a和d複製到a2中,而靜態變數num不會被複製。

與下面**等效:

a2.a=a1.a;

a2.d=a1.d;

注意:這只是說明,事實上是無法訪問私有成員的!

這就是預設的複製建構函式,但是有些情況下它福安滿足我們的需要,比如str類物件,裡面的資料成員是char指標和int整形,int整形很好辦,但是char指標只會把char指標儲存的位址複製過去,這樣當被複製的物件s1呼叫了析構函式時,呼叫複製的s2的str指標指向的內容也會消失。

所以我們需要自定義複製建構函式:

str(const str &s)

str s4(s2);

str s4=s2;

str s4=str(s2);

str *p=new str(s2);

上述情況都會呼叫複製建構函式。第一種情況是常見的,直接呼叫複製建構函式把s2的內容複製到s4。第二種和第三種會出現兩種情況,一是直接使用複製建構函式建立s4,二是先使用複製建構函式先生成乙個臨時變數,然後再s2的內容賦給s4,這取決於編譯器。第四種情況會生成乙個物件,然後把物件的位址賦給指標p。

如果將已有的物件賦值給另乙個物件,那就會呼叫賦值運算子,例如:

str s5;

s5=s2;

這樣進行賦值就會呼叫賦值建構函式,相當於呼叫了s5.operator=(s2);

賦值建構函式是通過過載"="來完成的:

返回值 operator=(引數列表);

有時會把複製建構函式與賦值建構函式混繞例如:

str s1=s2;

他到底是使用賦值還是複製?若在定義變數時賦初始值,那就是使用複製建構函式。

若只是使用=號賦值,如s1=s2;那就是賦值建構函式。

位址運算子就是&,他會取得物件的位址,他不需要顯式的提供,c++會預設地提供該功能。

char *operator&()

char *p=&s2;

下面談談成員初始化列表來初始化類:

str() :str(null), n(0){}

這樣當使用str類定義物件時,預設建構函式會初始化裡面的內容

在cpp檔案裡格式是:

類名::類名(引數列表):資料成員1(引數1),資料成員2(引數2)...資料成員n(引數n)

//函式體

這種格式只能用於建構函式,其他函式非法,必須用這種格式來初始化非靜態的const資料成員,必須使用這種格式裡初始化引用資料成員(型別名 &變數名)

c++11還允許在類裡初始化資料成員:

class a

;

相當於:a():a(0),b(0.0),n(10){}

C 類和動態記憶體分配

預設析構函式 複製建構函式 賦值運算子 位址運算子 ifndef zhumeng h define zhumeng h include class zhumeng endif include zhumeng.h int zhumeng numm 0 zhumeng zhumeng zhumeng z...

C 類和動態記憶體分配

如果有這樣的strbad類 include ifdef strbad h define srebad h class strbad endif include include stringbad.h using std cout int strbad num 0 靜態成員變數不能在宣告中初始化 除非...

類和動態記憶體分配

part 1 靜態類成員特點 無論建立了多少物件,程式都只建立乙個靜態類變數副本。也就是說,類的所有物件共享乙個靜態成員。靜態資料成員在類宣告中宣告,在包含類方法的檔案中初始化。初始化時使用作用域運算子來指出靜態成員所屬的類。但如果靜態成員是const整數型別或列舉型,則可以在類宣告中初始化。par...