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;