C++基礎:類與對象(構造 析構 賦值 拷貝構造)

C++中類的構造函數與析構函數,對象的賦值與複製,拷貝構造函數等。

和上一節一樣,還是用Kid類來說明

 

建立對象並初始化:Kid kid(10,"rookie_j","男");另外一種使用new運算符動態建立對象:Kid *ptr=new Kid(10,"rookie_j","男");通過指針變量ptr來訪問:ptr->showKid();當我們用new建立對象時,當不再使用它時,要用delete運算符釋放它:delete ptr;和不同成員函數一樣,如果構造函數定義在類體內部,則作爲內聯函數處理;

  在聲明類時,對數據成員的初始化一般在構造函數中用賦值語句進行,但C++還提供了另外一種初始化數據成員的方法——用成員初始化表來實現對數據成員的初始化。它的一般形式爲:類名::構造函數名([參數表]):[(成員初始化表)];成員初始化表的形式爲:成員名1(初始值1),成員名2(初始值2),成員名2(初始值2);比如:

Kid::Kid(int age,char *name,char *sex):age(age),name(name),sex(sex){};

  接下來講一下析構函數:在我第一次在C++裏看到這個名詞時,感覺這個知識點很深奧,結果看了以後,其實很簡單。它的作用和構造函數剛好相反,用於撤銷對象,如:釋放分配給對象的內存空間。析構函數和構造函數名相同,但在其前面要加~符號,析構函數沒有參數,也沒有返回值,且不能重載,因此一個類中只有一個析構函數。以下三種情況,當對象的生命週期結束時,析構函數會被自動調用:(1)定義了全局對象,則在程序流程離開其作用域(如:main函數結束或調用Exit)時,調用該全局對象的析構函數;(2)對象被定義在函數體裏,則當這個函數執行完後,該對象釋放,析構函數被自動調用;(3)若一個對象使用new運算符動態創建的,在使用delete運算符釋放它時,會自動調用析構函數;

View Code 
 #include "stdafx.h"
 #include <iostream>
 
 usingnamespace std;
 
 class Kid
 {
 private:
 int age;
 char*name;
 char*sex;
 public:
         Kid(int age,char*name,char*sex);
 ~Kid();
 void showKid();
 };
 
 
 Kid::Kid(int age,char*name,char*sex)
 {
     Kid::age=age;
     Kid::name=newchar[strlen(name)];
     strcpy(Kid::name,name);
     Kid::sex=newchar[strlen(sex)];
     strcpy(Kid::sex,sex);
 }
 
 Kid::~Kid()
 {
     cout<<"dispose object kid"<<endl;
     delete []name;
     delete []sex;
 }
 
 void Kid::showKid()
 {
     cout<<"姓名:"<<name<<endl<<"年齡:"<<age<<endl<<"性別:"<<sex<<endl;
 }
 
 
 int main()
 {
     Kid kid(10,"rookie_j","男");
     kid.showKid();
 
     Kid *ptr=new Kid(10,"rookie_x","女");
     ptr->showKid();
 
     delete ptr;
 
 return0;
 }

結果:

如果沒有給類定義構造函數,則編譯系統自動地生成一個默認的構造函數,比如在Kid類中編譯系統會爲其產生一個Kid::Kid(){};構造函數,這個默認的構造函數只能給對象開闢存儲空間,不能給數據成員賦值,這時數據成員的初值就是隨機數。對沒有定義構造函數的類,其公有數據成員可以用初始化值表進行初始化,如:

class Kid
 {
 public:
 int age;
 char*name;
 char*sex;
 };
 
 int main()
 {
 
     Kid kid={10,"Rookie_j","男"};
     cout<<"姓名:"<<kid.name<<endl<<"年齡:"<<kid.age<<endl<<"性別:"<<kid.sex<<endl;
 
 return0;
 }

但只要一個類定義了構造函數,系統將不再給它提供默認構造函數;另外還有默認的析構函數(Kid::~Kid(){})一般來說默認的析構函數就能滿足要求,但對一些需要做一些內部處理的則應該顯式定義析構函數。帶默認參數的構造函數和之前所說的帶參數的成員函數是一樣的,對於構造函數的重載,在這裏我就不多說了,只想強調一點,如果是無參的構造函數創建對象,應該使用"類名 對象名"的形式,而不是"類名 對象名()";

 

2.對象的賦值其實和變量的賦值差不多,也是用賦值運算符=進行的,只不過進行賦值的兩個對象的類型必須相同,對象之間的賦值只是數據成員的賦值,而不對成員函數賦值;

View Code 
 #include "stdafx.h"
 #include <iostream>
 
 usingnamespace std;
 
 class Kid
 {
 private:
 int age;
 char*name;
 char*sex;
 public:
         Kid(int age,char*name,char*sex);
         Kid(){    };
 ~Kid();
 void showKid();
 };
 
 
 Kid::Kid(int age,char*name,char*sex)
 {
     Kid::age=age;
     Kid::name=newchar[strlen(name)];
     strcpy(Kid::name,name);
     Kid::sex=newchar[strlen(sex)];
     strcpy(Kid::sex,sex);
 }
 
 Kid::~Kid()
 {
     cout<<"dispose object kid"<<endl;
     delete []name;
     delete []sex;
 }
 
 void Kid::showKid()
 {
     cout<<"姓名:"<<name<<endl<<"年齡:"<<age<<endl<<"性別:"<<sex<<endl;
 }
 
 int main()
 {
     Kid kid(10,"rookie_j","男"),kid2;
     kid.showKid();
 
     kid2=kid;
     kid2.showKid();
 
 return0;
 }


 

結果:

 

拷貝構造函數是一種特殊的構造函數,其形參是類對象的引用。它主要用於在建立一個新的對象時,使用已經存在的對象去初始化這個新對象。拷貝構造函數也是構造函數,所以函數名必須與類名相同,參數只有一個就是同類對象的引用,每個類必須要有一個拷貝構造函數。如果程序員自己不定義拷貝構造函數,系統會自動產生一個默認拷貝構造函數。調用拷貝構造函數的形式有代入法:類名 對象2(對象1)和賦值法:類名 對象2=對象1;

View Code 
 #include "stdafx.h"
 #include <iostream>
 
 usingnamespace std;
 
 class Kid
 {
 private:
 int age;
 char*name;
 char*sex;
 public:
         Kid(int age,char*name,char*sex);
         Kid(const Kid &kid);
 ~Kid();
 void showKid();
 };
 
 
 Kid::Kid(int age,char*name,char*sex)
 {
     Kid::age=age;
     Kid::name=newchar[strlen(name)];
     strcpy(Kid::name,name);
     Kid::sex=newchar[strlen(sex)];
     strcpy(Kid::sex,sex);
 }
 
 Kid::Kid(const Kid &kid)
 {
     Kid::age=kid.age*2;
     Kid::name=newchar[strlen(kid.name)];
     strcpy(Kid::name,kid.name);
     Kid::sex=newchar[strlen(kid.sex)];
     strcpy(Kid::sex,kid.sex);
 }
 
 Kid::~Kid()
 {
     cout<<"dispose object kid"<<endl;
     delete []name;
     delete []sex;
 }
 
 void Kid::showKid()
 {
     cout<<"姓名:"<<name<<endl<<"年齡:"<<age<<endl<<"性別:"<<sex<<endl;
 }
 
 int main()
 {
     Kid kid(10,"rookie_j","男");
     kid.showKid();
 
     Kid kid2(kid);
     kid2.showKid();
 
     Kid kid3=kid2;
     kid3.showKid();
 
 return0;
 }


 

結果:

 

同樣的默認的拷貝構造函數:複製出與源對象的數據成員的值一樣的新對象。調用拷貝構造函數的3種情況:(1)Kid kid2(kid1)或Kid kid2=kid1;(2)函數的形參是類的對象:fun(Kid kid){kid.showKid();}; int main(){Kid kid(10,"Rookie_j","男");fun(kid);return 0;};(3)函數返回值爲類的對象:Kid fun(){Kid kid(10,"Rookie_j","男"); return kid;} int main(){ Kid kid; kid=fun();kid.showKid();return 0;};

----------------------------------------------------------------------

已知類String的原型爲:
    class String
    {
     public:
        String(const char *str = NULL); // 
普通構造函數
        String(const String &other);        // 
拷貝構造函數
        ~ String(void);                     // 
析構函數
        String & operate =(const String &other);    // 
賦值函數
     private:
        char  *m_data;                // 
用於保存字符串
    };
       
請編寫String的上述4個函數。
標準答案:
 
// String
的析構函數
       String::~String(void)               // 3

{
    delete [] m_data;                      
// 
由於m_data是內部數據類型,也可以寫成 delete m_data;
       }
       // String
的普通構造函數             
       String::String(const char *str)      // 6

{
    if(str==NULL)                          
    {
       m_data = new char[1];    // 
若能加 NULL 判斷則更好
       *m_data = ‘\0’;                      
    }                                          
    else
    {
       int length = strlen(str);           
       m_data = new char[length+1]; // 
若能加 NULL 判斷則更好      
       strcpy(m_data, str);                
    }
}   
// 
拷貝構造函數
    String::String(const String &other)   // 3

    {   
    int length = strlen(other.m_data); 
    m_data = new char[length+1];      // 
若能加 NULL 判斷則更好    
    strcpy(m_data, other.m_data);         
}
// 
賦值函數
    String & String::operate =(const String &other)    // 13

    {   
       // (1) 
檢查自賦值                     // 4
       if(this == &other)
           return *this;//
文章來源 草根IT(www.caogenit.com)
    
// (2) 
釋放原有的內存資源            // 3
       delete [] m_data;
       
       // 
3)分配新的內存資源,並複製內容 // 3
    int length = strlen(other.m_data); 
    m_data = new char[length+1];         // 
若能加 NULL 判斷則更好
       strcpy(m_data, other.m_data);
       // 
4)返回本對象的引用            // 3
       return *this;

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章