[C++]類和物件(三)

丫就是熊個貓貓發表於2016-12-09

類和物件

預設的建構函式

在定義類時,若沒有定義類的建構函式,則編譯器自動產生一個預設的建構函式,其格式為:

className::className() {  }

預設的建構函式並不對所產生物件的資料成員賦初值;即新產生物件的資料成員的值是不確定的。

class A{

float  x,y;

public:

A(){}//預設的建構函式,編譯器自動產生,可以不寫

float Sum(void) {   return  x+y;   }

void Set(float a,float b) {    x=a; y=b;}

      void Print(void) { cout<<"x="<<x<<'\t'<<"y="<<y<<endl;   }

};

void main(void)

{ A a1,a2;//產生物件時,自動呼叫預設的建構函式,不賦值

a1.Set (2.0,4.0);

cout<<"a1:  ";

   a1.Print ();

cout<<"a1.sum="<<a1.Sum ()<<endl;

a2.Print();//列印隨機值

}

關於預設的建構函式,說明以下幾點:

1、在定義類時,只要顯式定義了一個類的建構函式,則編譯器就不產生預設的建構函式

2、所有的物件在定義時,必須呼叫建構函式

不存在沒有建構函式的物件!

class A{

float x,y;

public:

   A(float a,float b){x=a;y=b;}    顯式定義了建構函式,不產生預設的建構函式

   void Print(void){cout<<x<<'\t'<<y<<endl;}

};

void main(void)

{A  a1;           error,定義時,沒有建構函式可供呼叫

A  a2(3.0,30.0);

}

3、在類中,若定義了沒有引數的建構函式,或各引數均有預設值的建構函式也稱為預設的建構函式,預設的建構函式只能有一個。

4、產生物件時,系統必定要呼叫建構函式。所以任一物件的建構函式必須唯一。

class A{

float x,y;

public:

    A(float a=10,float b=20){x=a;y=b;}          兩個函式均為預設的建構函式

    A(){  }

   void Print(void){cout<<x<<'\t'<<y<<endl;}

};

void main(void)

{A  a1;            兩個建構函式均可供呼叫,建構函式不唯一

A  a2(3.0,30.0);

}

建構函式與new運算子

·以使用new運算子來動態地建立物件。建立時要自動呼叫建構函式,以便完成初始化物件的資料成員。最後返回這個動態物件的起始地址。

·用new運算子產生的動態物件,在不再使用這種物件時,必須用delete運算子來釋放物件所佔用的儲存空間。

·用new建立類的物件時,可以使用引數初始化動態空間。

class  A{

float   x,y;

public:

A(float a, float b){x=a;y=b;}

A(){x=0;  y=0;}

void  Print(void){  cout<<x<<'\t'<<y<<endl;  }

};

void main(void)

{   A   *pa1,*pa2;

    pa1=new  A(3.0, 5.0);//用new動態開闢物件空間,初始化

    pa2=new A;//用new動態開闢空間,呼叫建構函式初始化

    pa1->Print();

    pa2->Print();

    delete  pa1;  //用delete釋放空間

    delete  pa2; //用delete釋放空間

}

解構函式

解構函式的作用與建構函式正好相反,是在物件的生命期結束時,釋放系統為物件所分配的空間,即要撤消一個物件。

解構函式也是類的成員函式,定義解構函式的格式為:

ClassName::~ClassName( )

{

   ......

//         函式體;

          }

析構 函式的特點如下:

1、解構函式是成員函式,函式體可寫在類體內,也可寫在類體外。

2、解構函式是一個特殊的成員函式,函式名必須與類名相同,並在其前面加上字元“~”,以便和建構函式名相區別。

3、解構函式不能帶有任何引數,不能有返回值,不指定函式型別。

4、一個類中,只能定義一個解構函式,解構函式不允許過載。

5、解構函式是在撤消物件時由系統自動呼叫的。

在程式的執行過程中,當遇到某一物件的生存期結束時,系統自動呼叫解構函式,然後再收回為物件分配的儲存空間。

class A{

float x,y;

public:

   A(float a,float b)

 {x=a;y=b;cout<<"呼叫非預設的建構函式\n";}

   A() {  x=0;  y=0;  cout<<"呼叫預設的建構函式\n" ;}

   ~A() {cout<<"呼叫解構函式\n";}

    void Print(void) {    cout<<x<<'\t'<<y<<endl;}

};

void main(void)

{A  a1;

A  a2(3.0,30.0);

cout<<"退出主函式\n";

}

呼叫預設的建構函式

呼叫非預設的建構函式

退出主函式

呼叫解構函式

呼叫解構函式

   在程式的執行過程中,物件如果用new運算子開闢了空間,則在類中應該定義一個解構函式,並在解構函式中使用delete刪除由new分配的記憶體空間。因為在撤消物件時,系統自動收回為物件所分配的儲存空間,而不能自動收回由new分配的動態儲存空間。

用new運算子為物件分配動態儲存空間時,呼叫了建構函式,用delete刪除這個空間時,呼叫了解構函式。當使用運算子delete刪除一個由new動態產生的物件時,它首先呼叫該物件的解構函式,然後再釋放這個物件佔用的記憶體空間。

class  A{

float   x,y;

public:

  A(float a, float b){x=a;y=b;cout<<"呼叫了建構函式\n";}

  void  Print(void){  cout<<x<<'\t'<<y<<endl;  }

  ~A(){  cout<<"呼叫了解構函式\n";   }

};

void main(void)                                 進入main()函式

{  cout<<"進入main()函式\n";                    呼叫了建構函式

    A   *pa1;                              

    pa1=new  A(3.0, 5.0);//呼叫建構函式          3     5

    pa1->Print(); 

    delete  pa1;  //呼叫解構函式               呼叫了解構函式

    cout<<"退出main()函式\n";                  退出main()函式

}

 

不同儲存型別的物件呼叫建構函式及解構函式

 1、對於全域性定義的物件(在函式外定義的物件),在程式開始執行時,呼叫建構函式;到程式結束時,呼叫解構函式。

2、對於區域性定義的物件(在函式內定義的物件),當程式執行到定義物件的地方時,呼叫建構函式;在退出物件的作用域時,呼叫解構函式。

3、用static定義的區域性物件,在首次到達物件的定義時呼叫建構函式;到程式結束時,呼叫解構函式

 4、對於用new運算子動態生成的物件,在產生物件時呼叫建構函式,只有使用delete運算子來釋放物件時,才呼叫解構函式。若不使用delete來撤消動態生成的物件,程式結束時,物件仍存在,並佔用相應的儲存空間,即系統不能自動地呼叫解構函式來撤消動態生成的物件。

 

動態構造及析構物件陣列

用new運算子來動態生成物件陣列時,自動呼叫建構函式,而用delete運算子來釋放p1所指向的物件陣列佔用的儲存空間時,在指標變數的前面必須加上[ ], 才能將陣列元素所佔用的空間全部釋放。否則,只釋放第0個元素所佔用的空間。

pa1=new  A[3];

.....

delete  [ ]pa1;

預設的解構函式

若在類的定義中沒有顯式地定義解構函式時,則編譯器自動地產生一個預設的解構函式,其格式為:ClassName::~ClassName() { };

任何物件都必須有建構函式和解構函式,但在撤消物件時,要釋放物件的資料成員用new運算子分配的動態空間時,必須顯式地定義解構函式。

實現型別轉換的建構函式

同型別的物件可以相互賦值,相當於類中的資料成員相互賦值;

如果直接將資料賦給物件,所賦入的資料需要強制型別轉換,這種轉換需要呼叫建構函式。

 

完成拷貝功能的建構函式

可以在定義一個物件的時候用另一個物件為其初始化,即建構函式的引數是另一個物件的引用,這種建構函式常為完成拷貝功能的建構函式。

完成拷貝功能的建構函式的一般格式為:

ClassName::ClassName(ClassName  &<變數名>)

{......

//   函式體完成對應資料成員的賦值

}

如果沒有定義完成拷貝功能的建構函式,編譯器自動生成一個隱含的完成拷貝功能的建構函式,依次完成類中對應資料成員的拷貝。

A::A(A &a)

{

       x=a.x;

       y=a.y;

 }

由編譯器為每個類產生的這種隱含的完成拷貝功能的建構函式,依次完成類中對應資料成員的拷貝。

但是,當類中的資料成員中使用new運算子,動態地申請儲存空間進行賦初值時,必須在類中顯式地定義一個完成拷貝功能的建構函式,以便正確實現資料成員的複製。

 

 

建構函式與物件成員

 

對類A的物件初始化的同時還要對其成員資料類B的物件進行初始化,所以,類A的建構函式中要呼叫類B的建構函式。

ClassName::ClassName(args):c1(args1),..,cn(agrsn)

{

...... //對其它成員的初始化

}

初始化物件成員的引數(實參)可以是表示式。,也可以僅對部分物件成員進行初始化。

 

對物件成員的建構函式的呼叫順序取決於這些物件成員在類中說明的順序,與它們在成員初始化列表中的順序無關。

當建立類ClassName的物件時,先呼叫各個物件成員的建構函式,初始化相應的物件成員,然後才執行類ClassName的建構函式,初始化類ClassName中的其它成員。解構函式的呼叫順序與建構函式正好相反。

 

 

相關文章