1. 首頁
  2. 語文

計算機二級考試C++複習資料

計算機二級考試C++複習資料

一、解構函式

前面的一些例子都沒有說明解構函式,這是因為所用到的類在結束時不需要做特別的清理工作。下面的程式給出了一新的Date類,其中包括一個字串指標,用來表示月份。

#include iostream.h

#include string.h

class Date

{

int mo,da,yr;

char *month;

public:

Date(int m=0, int d=0, int y=0);

~Date();

void display() const;

};

Date::Date(int m,int d,int y)

{

static char *mos[] =

{

January,February,March,April,May,June,

July,August,September,October,November,December

};

mo=m; da=d; yr=y;

if(m!=0)

{

month=new char[strlen(mos[m-1])+1];

strcpy(month, mos[m-1]);

}

else month = 0;

}

Date::~Date()

{

[] month;

}

void Date::display() const

{

if(month!=0) cout< }

int main()

{

Date birthday(8,11,1979);

birthday.display();

return 0;

}

在Date物件的建構函式中,首先用new運算子為字串month動態分配了記憶體,然後從內部陣列中把月份的名字複製給字串指標month。

解構函式在刪除month指標時,可能會出現一些問題。當然從這個程式本身來看,沒什麼麻煩;但是從設計一個類的角度來看,當Date類用於賦值時,就會出現問題。假設上面的main()修改為“

int main()

{

Date birthday(8,11,1979);

Date today;

today=birthday;

birthday.display();

return 0;

}

這會生成一個名為today的空的Date型變數,並且把birthday值賦給它。如果不特別通知編譯器,它會簡單的認為類的賦值就是成員對成員的複製。在上面的程式中,變數birthday有一個字元型指標month,並且在構造函數里用new運算子初始化過了。當birthday離開其作用域時,解構函式會呼叫運算子來釋放記憶體。但同時,當today離開它的作用域時,解構函式同樣會對它進行釋放操作,而today裡的month指標是birthday裡的month指標的一個複製。解構函式對同一指標進行了兩次刪除操作,這會帶來不可預知的後果。

如果假設today是一個外部變數,而birthday是一個自變數。當birthday離開其作用域時,就已經把物件today裡的month指標刪除了。顯然這也是不正確的。

再假設有兩個初始化的Date變數,把其中一個的值賦值給另一個:

Date birthday(8,11,1979);

Date today(12,29,2003);

today=birthday;

問題就更復雜了,當這兩個變數離開作用域時,birthday中的month的值已經透過賦值傳遞給了today。而today中建構函式用new運算子給month的值卻因為賦值被覆蓋了。這樣,birthday中的month被刪除了兩次,而today中month卻沒有被刪除掉。

二、過載賦值運算子

為了解決上面的問題,我們應該寫一個特殊的賦值運算子函式來處理這類問題。當需要為同一個類的兩個物件相互賦值時,就可以過載運算子函式。這個方法可以解決類的賦值和指標的釋放。

下面的程式中,類中的賦值函式用new運算子從堆中分配了一個不同的指標,該指標獲取賦值物件中相應的值,然後複製給接受賦值的物件。

在類中過載賦值運算子的格式如下:

void operator = (const Date&)

後面我們回加以改進。目前,過載的運算子函式的返回型別為void。它是類總的成員函式,在本程式紅,是Date類的成員函式。它的函式名始終是operator =,引數也始終是同一個類的物件的引用。引數表示的是源物件,即賦值資料的提供者。過載函式的運算子作為目標物件的成員函式來使用。

#include iostream.h

#include string.h

class Date

{

int mo,da,yr;

char *month;

public:

Date(int m=0, int d=0, int y=0);

~Date();

void operator=(const Date&);

void display() const;

};

Date::Date(int m, int d, int y)

{

static char *mos[] =

{

January,February,March,April,May,June,

July,August,September,October,November,December

};

mo = m; da = d; yr = y;

if (m != 0)

{

month = new char[strlen(mos[m-1])+1];

strcpy(month, mos[m-1]);

}

else month = 0;

}

Date::~Date()

{

[] month;

}

void Date::display() const

{

if (month!=0) cout< char name[25];

cin >> name;

if (strncmp(name, end, 3) == 0) break;

ListEntry* list = new ListEntry(name);

if (prev != 0) prev->AddEntry(*list);

prev = list;

}

while (prev != 0)

{

prev->display();

ListEntry* hold = prev;

prev = prev->PrevEntry();

hold;

}

return 0;

}

程式執行時,會提示輸入一串姓名,當輸入完畢後,鍵入end,然後程式會逆序顯示剛才輸入的`所有姓名。

程式中ListEntry類含有一個字串和一個指向前一個表項的指標。建構函式從對中獲取記憶體分配給字串,並把字串的內容複製到記憶體,然後置連結指標為NULL。解構函式將釋放字串所佔用的記憶體。

成員函式PrevEntry()返回指向連結串列前一個表項的指標。另一個成員函式顯示當前的表項內容。

成員函式AddEntry(),它把this指標複製給引數的preventry指標,即把當前表項的地址賦值給下一個表項的連結指標,從而構造了一個連結串列。它並沒有改變呼叫它的listEntry物件的內容,只是把該物件的地址賦給函式的引數所引用的那個ListEntry物件的preventry指標,儘管該函式不會修改物件的資料,但它並不是常量型。這是因為,它複製物件的地址this指標的內容給一個非長常量物件,而編譯器回認為這個非常量物件就有可能透過複製得到的地址去修改當前物件的資料,因此AddEntry()函式在宣告時不需要用const。