第一篇:2.3實驗二、三 構造函數與析構函數
南昌航空大學實驗報告
年月日 課程名稱:面向對象程序設計實驗名稱:構造函數與析構函數班級: XXXXXXX學生姓名:XXXXXX學號:指導教師評定:XX簽名:XXXXX1、實驗目的·理解類與對象的區別。
·學習掌握定義構造函數與析構函數的方法。
·學習把握默認構造函數的意義。
·了解類成員初始化,掌握構造與析構類成員的方式。
2、實驗內容
(1)
創建一個Employee類,該類中用字符數組存放Employee的信息,如姓名、地址、市、省、及郵政編碼;每個成員函數的定義放在類定義之外;成員函數包括改變姓名數據成員等;構造函數完成成員數據的初始化;用Display()函數將完整的對象數據打印出來;其中數據成員是保護的,成員函數是公共的。
#include
#include
#include
class Employee
{
public:
};Employee(char n[20],char a[50],char s[20],char sh[20],char p[6]);void ChangeName(char ch_name[20]);void Display();char name[20];char address[50];char shi[20];char sheng[20];char post[6];protected:
給出各個成員函數的具體實現
void main()
{
Employee em(“rer r34t3”,“5 West St.”,“Revere”,“CA”,“12290”);em.Display();em.ChangeName(“sdfesfefe”);
}
em.Display();
(2)設計一個表示矩形的類Rect,其矩形成員長float * Length ,寬float * Width為指針變量,設計相應成員函數,實現下列功能:
① 構造函數設置長和寬(默認為1)。
② 復制構造函數用老對象生成新對象。
③ set()函數設置長和寬(默認為0)。
④ 計算并返回長方形的周長。
⑤ 計算并返回長方形的面積。
⑥ 析構函數釋放動態分配的長和寬。
編制主程序應用指針建立對象測試類。
#include
class Rect{
float *Length,*Width;
public:
Rect(float a=1,float b=1);
Rect(Rect &r);
void set(float a=0,float b=0);
float peri();
float area();
~Rect(){delete Length;delete Width;}
};
補充其余代碼
//主函數
void main()
{ Rect r1(55.5,40);
cout<<“周長為:”< cout<<“---------------------n”; Rect *p=new Rect(r1);//思考:此處改為 Rect *p=new Rect;下一行的輸出會是什么結果 cout<<“周長為:”< peri()<<“面積為: ”< area()< p->set(20.5,13); cout<<“周長為:”< peri()<<“面積為: ”< area()< } 類是編程人員表達自定義數據類型的C++機制。它和C語言中的結構類似,C++類支持數據抽象和面向對象的程序設計,從某種意義上說,也就是數據類型的設計和實現。 一、類的設計 1.類的聲明 class 類名 { private://私有 ...public://公有 ...}; 2.類的成員 一般在C++類中,所有定義的變量和函數都是類的成員。如果是變量,我們就叫它數據成員如果是函數,我們就叫它成員函數。 3.類成員的可見性 private和public訪問控制符決定了成員的可見性。由一個訪問控制符設定的可訪問狀態將一直持續到下一個訪問控制符出現,或者類聲明的結束。私有成員僅能被同一個類中的成員函數訪問,公有成員既可以被同一類中的成員函數訪問,也可以被其他已經實例化的類中函數訪問。當然,這也有例外的情況,這是以后要討論的友元函數。類中默認的數據類型是private,結構中的默認類型是public。一般情況下,變量都作為私有成員出現,函數都作為公有成員出現。 類中還有一種訪問控制符protected,叫保護成員,以后再說明。 4.初始化 在聲明一個類的對象時,可以用圓括號()包含一個初始化表。 看下面一個例子: #include “iostream.h” class Box { private: int height,width,depth;//3個私有數據成員 public: Box(int,int,int); ~Box(); int volume();//成員函數 }; Box::Box(int ht,int wd,int dp) { height=ht; width=wd; depth=dp; } Box::~Box() { //nothing } int Box::volume() { return height*width*depth; } int main() { Box thisbox(3,4,5);//聲明一個類對象并初始化 cout< return 0; } 當一個類中沒有private成員和protected成員時,也沒有虛函數,并且不是從其他類中派生出來的,可以用{}來初始化。(以后再講解) 5.內聯函數 內聯函數和普通函數的區別是:內聯函數是在編譯過程中展開的。通常內聯函數必須簡短。定義類的內聯函數有兩種方法:一種和C語言一樣,在定義函數時使用關鍵字inline。如: inline int Box::volume() { return height*width*depth; } 還有一種方法就是直接在類聲明的內部定義函數體,而不是僅僅給出一個函數原型。我們把上面的函數簡化一下: #include “iostream.h” class Box { private: int height,width,depth; public: Box(int ht,int wd,int dp) { height=ht; width=wd; depth=dp; } ~Box(); int volume() { return height*width*depth; } }; int main() { Box thisbox(3,4,5);//聲明一個類對象并初始化 cout< return 0; } 這樣,兩個函數都默認為內聯函數了。 二、構造函數 什么是構造函數?通俗的講,在類中,函數名和類名相同的函數稱為構造函數。上面的Box()函數就是構造函數。C++允許同名函數,也就允許在一個類中有多個構造函數。如果一個都沒有,編譯器將為該類產生一個默認的構造函數,這個構造函數可能會完成一些工作,也可能什么都不做。 絕對不能指定構造函數的類型,即使是void型都不可以。實際上構造函數默認為void型。 當一個類的對象進入作用域時,系統會為其數據成員分配足夠的內存,但是系統不一定將其初始化。和內部數據類型對象一樣,外部對象的數據成員總是初始化為0。局部對象不會被初始化。構造函數就是被用來進行初始化工作的。當自動類型的類對象離開其作用域時,所站用的內存將釋放回系統。 看上面的例子,構造函數Box()函數接受三個整型擦黑素,并把他們賦值給立方體對象的數據成員。 如果構造函數沒有參數,那么聲明對象時也不需要括號。 1.使用默認參數的構造函數 當在聲明類對象時,如果沒有指定參數,則使用默認參數來初始化對象。 #include “iostream.h” class Box { private: int height,width,depth; public: Box(int ht=2,int wd=3,int dp=4) { height=ht; width=wd; depth=dp; } ~Box(); int volume() { return height*width*depth; } }; int main() { Box thisbox(3,4,5);//初始化 Box defaulbox;//使用默認參數 cout< cout< 4return 0; } 2.默認構造函數 沒有參數或者參數都是默認值的構造函數稱為默認構造函數。如果你不提供構造函數,編譯器會自動產生一個公共的默認構造函數,這個構造函數什么都不做。如果至少提供一個構造函數,則編譯器就不會產生默認構造函數。 3.重載構造函數 一個類中可以有多個構造函數。這些構造函數必須具有不同的參數表。在一個類中需要接受不同初始化值時,就需要編寫多個構造函數,但有時候只需要一個不帶初始值的空的Box對象。 #include “iostream.h” class Box { private: int height,width,depth; public: Box(){ //nothing } Box(int ht=2,int wd=3,int dp=4) { height=ht; width=wd; depth=dp; } ~Box(); int volume() { return height*width*depth; } }; int main() { Box thisbox(3,4,5);//初始化 Box otherbox; otherbox=thisbox; cout< return 0; } 這兩個構造函數一個沒有初始化值,一個有。當沒有初始化值時,程序使用默認值,即2,3,4。 但是這樣的程序是不好的。它允許使用初始化過的和沒有初始化過的Box對象,但它沒有考慮當thisbox給otherbox賦值失敗后,volume()該返回什么。較好的方法是,沒有參數表的構造函數也把默認值賦值給對象。 class Box { int height,width,depth; public: Box() { height=0;width=0;depth=0; } Box(int ht,int wd,int dp) { height=ht;width=wd;depth=dp; } int volume() { return height*width*depth; } }; 這還不是最好的方法,更好的方法是使用默認參數,根本不需要不帶參數的構造函數。 class Box { int height,width,depth; public: Box(int ht=0,int wd=0,int dp=0) { height=ht;width=wd;depth=dp; } int volume() { return height*width*depth; } }; 三、析構函數 當一個類的對象離開作用域時,析構函數將被調用(系統自動調用)。析構函數的名字和類名一樣,不過要在前面加上 ~。對一個類來說,只能允許一個析構函數,析構函數不能有參數,并且也沒有返回值。析構函數的作用是完成一個清理工作,如釋放從堆中分配的內存。 我們也可以只給出析構函數的形式,而不給出起具體函數體,其效果是一樣的,如上面的例子。但在有些情況下,析構函數又是必需的。如在類中從堆中分配了內存,則必須在析構函數中釋放 關于構造函數與析構函數的說明 ? 構造函數、析構函數一定有。 ? 子類構造函數(開始時)一定會調用父類構造函數。? 子類析構函數(結束時)一定會調用父類析構函數。 1.如果沒有定義構造函數,C++會自動添加默認構造函數(即無參的構造函數,只負責分配空間,不負責數據的初始化值)。 2.如果有定義的構造函數(不管有參的還是無參的),C++不會再自動添加默認構造函數。 3.子類的構造函數一定會調用父類構造函數,在不指定的情況下,自動調用無參的構造函數。 4.如果沒有定義析構函數,C++會自動添加默認析構函數。 5.子類析構函數結束時會自動調用父類析構函數。 類的構造函數 析構函數與賦值函數 構造函數 析構函數與賦值函數是每個類最基本的函數。它們太普通以致讓人容易麻痹大意,其實這些貌似簡單的函數就象沒有頂蓋的下水道那樣危險。 每個類只有一個析構函數和一個賦值函數,但可以有多個構造函數(包含一個拷貝構造函數,其它的稱為普通構造函數)。對于任意一個類A,如果不想編寫上述函數,C++編譯器將自動為A產生四個缺省的函數,如 A(void);// 缺省的無參數構造函數 A(const A &a);// 缺省的拷貝構造函數 ~A(void);// 缺省的析構函數 A & operate =(const A &a);// 缺省的賦值函數 這不禁讓人疑惑,既然能自動生成函數,為什么還要程序員編寫? 原因如下: (1)如果使用“缺省的無參數構造函數”和“缺省的析構函數”,等于放棄了自主“初始化”和“清除”的機會,C++發明人Stroustrup的好心好意白費了。 (2)“缺省的拷貝構造函數”和“缺省的賦值函數”均采用“位拷貝”而非“值拷貝”的方式來實現,倘若類中含有指針變量,這兩個函數注定將出錯。 對于那些沒有吃夠苦頭的C++程序員,如果他說編寫構造函數 析構函數與賦值函數很容易,可以不用動腦筋,表明他的認識還比較膚淺,水平有待于提高。 本章以類String的設計與實現為例,深入闡述被很多教科書忽視了的道理。String的結構如下: class String { public: String(const char *str = NULL);// 普通構造函數 String(const String &other);// 拷貝構造函數 ~ String(void);// 析構函數 String & operate =(const String &other);// 賦值函數private: char*m_data;// 用于保存字符串 }; C++繼承中構造函數、析構函數調用順序及虛函數的動態綁定 昨天面試被問到這些,慚愧的很,居然搞混了,悔恨了一把。決定要徹底搞清楚。也算是有所收獲。 首先說說構造函數,大家都知道構造函數里就可以調用成員變量,而繼承中子類是把基類的成員變成自己的成員,那么也就是說子類在構造函數里就可以調用基類的成員了,這就說明創建子類的時候必須先調用基類的構造函數,只有這樣子類才能在構造函數里使用基類的成員,所以是創建子類時先調用基類的構造函數然后再調用自己的構造函數。通俗點說,你要用某些物品,但這些物品你沒辦法自己生產,自然就要等別人生產出來,你才能拿來用。 接著就是析構函數了,上面說到子類是將基類的成員變成自己的成員,那么基類就會只存在子類中直到子類調用析構函數后。做個假設:假如在基類的析構函數調用比子類的先,這樣會發生什么事呢?類成員終止了,而類本身卻還在,但是在類存在的情況下,類成員就應該還存在的,這不就產生矛盾了嗎?所以子類是調用自身的析構函數再調用基類的析構函數。 現在到了虛函數了,virtual主要作用是在多態方面,而C++的多態最主要的是類的動態綁定,動態綁定則是指將子類的指針或引用轉換成基類對象,基類對象就可以動態判斷調用哪個子類成員函數。這就說明在沒有子類指針或引用轉換為基類對象的話,virtual沒有存在意義(純虛函數除外),也就是有沒有virtual都是調用其自身的成員函數。通過這些分析,對于virtual就有了眉目了。當子類指針或引用轉換為基類時,若基類中有用virtual定義的函數,被子類重寫后,此基類對象就會根據子類調用子類中的重寫后的函數,而不是基類中的函數;反之,若是基類中沒有用virtual定義,則不管基類被賦值的是哪個子類的值,調用的都是基類的成員函數(當然指的是子類重載的基類函數,不然就算要調用子類特有的成員函數也會編譯不過)。第二篇:二、類的設計,構造函數和析構函數
第三篇:關于C++構造函數與析構函數的說明
第四篇:類的構造函數 析構函數與賦值函數
第五篇:構造函數-析構函數的調用順序