構造函數:
是在對象已經具有空間以後做一些初始化的工作,不負責分配空間
是成員函數的一種,與類名相同,可以帶參,無返回值,對對象做初始化
類默認有構造函數,無參無操作
有對象生成時一定調用構造函數
一個類可有多個構造函數
class Rectangle:
{
private:
int w,h;
public:
Rectangle(int i, int j=0);
Rectangle();
Rectangle(Rectangle r1, Rectangle r2);
};
//三個構造函數
Rectangle::Rectangle(int i, int j=0){
w = i;
h = j;
}
Rectangle::Rectangle(){
w = 0;
h = 0;
}
Rectangle::Rectangle(Rectangle r1, Rectangle r2){
w = r1.w + r2.w;
h = r1.h + r2.h;
}
int main(){
//第二個,第一個,第三個構造函數
Rectangle r(),r1(1),r2(r,r1);
//Rectangle r; 也用第二個
}
構造函數在數組的使用
指針不會引發對象的生成:
Rectangle *r[3]; //不會調用任何構造函數,因爲是一個指針數組
Rectangle *r[3]={new Rectangle(4), NULL, new Rectangle(1,2)} //只生成兩個對象,new返回爲指針
複製構造函數
形如 X::X( X& x) 或 X::X(const X& x) ,參數爲同類對象的引用,無定義編譯器默認生成,完成兩個對象複製的功能
複製構造函數與無參構造函數區別:
1. 無參構造函數也稱默認構造函數,不一定存在(你不寫任何構造函數編譯器纔會默認生成)
2. 複製構造函數一定存在(要麼你寫要麼編譯器幫你寫),複製構造函數只有一個
class Complex:{
private:
double img,re;
};
int main(){
Complex c1;
Complex c2(c1); //調用缺省的默認的複製構造函數,c2與c1一樣
}
複製構造函數起作用的三種情況:
//1.
Complex c1;
//2.初始化同類的另一個對象
Complex c2(c1); //或Complex c2=c1;
//某函數Func()的參數是類A的對象,則Func調用時,類A的複製構造函數將被調用
class A:
{
public:
A(){};
A(A & a){cout<<"copy construct"<<endl};
};
void Func(A a1){}
int main(){
A a2;
Func(a2); //此時a1對象的初始化調用複製構造函數,但我們的複製構造函數不是拷貝,所以a1可
//能不是a2的拷貝
return 0;
}
//3.若函數Func的返回值爲A的對象時,Func返回時A的複製構造函數被調用
class A:
{
public:
int v;
A(int n){v=n;};
A(const A & a){
v = a.v
cout<<"copy construct"<<endl
};
};
A Func(){
A a1(3);
return a1;
}
int main(){
cout<<Func().v<<endl; //3
return 0;
}
對象間的賦值不等於複製構造函數被調用比如:
Complex c1;
c1=c2;
爲了減少函數調用時,複製構造函數調用所產生的時間開銷,可以用常量引用參數
void Func(const A & a ){}
引用的參數與實參是一回事,不會生成對象,若想讓函數內部操作改變形參不改變實參,加上const
類型轉換構造函數
爲了實現類型轉換
只有一個參數且不是複製構造函數
需要時編譯器自動調用,生成一個無名臨時對象
class Complex:
{
public:
double a,b;
Complex(double i, double j){a=i;b=j;}
Complex(int i){a=i; b=0;} //double to int
};
int main(){
Complex c1(7,8); //7.0,8.0
Complex c2=12; //a=12,b=0
c1=9; //編譯器將9自動轉換爲一個無名臨時對象其a=9,b=0,再將這個臨時對象賦給c1
return 0;
}
析構函數(destructor)
在對象消亡時被調用(可爲釋放空間等),一個類最多一個
若不寫編譯器會自動生成(什麼都不做)
函數名爲:‘~’ + ‘className’
無參無返回值
在對象的空間被回收前的操作
class String:
{
private:
char *p;
public:
String(){
p = new char[10];
}
~ String();
};
String::~ String(){
delete []p;
}
對象數組生命週期結束時,其中的每個元素的析構函數都會被調用
消亡的幾種情況:
//1. main函數結束時
class A:
{
public:
int a;
~ A(){cout<< "destructor" <<endl;}
};
int main(){
A a[2];
cout<< "end main" <<endl;
return 0;
}
//輸出結果爲:
//end main
//destructor
//destructor
//2.new出的對象,delete時消亡,不delete不消亡
A *a;
a = new A;
delete a; //析構函數調用
A a1[3];
delete [] a; //析構函數調用三次
//3. 對象作爲函數返回值,返回後調用析構函數
class A:
{
public:
int a;
~ A(){cout<< "destructor" <<endl;}
};
A func(A sa) //調用默認複製構造函數初始化形參sa
{
return sa; //返回形參sa後析構函數被調用
}
int main(){
A a;
a = func(a); //func(a)生成一個返回值對象(臨時對象),用複製構造函數初始化,其值賦
//值給a,執行完這條語句後,臨時對象消亡,調用析構函數
cout<< "end main" <<endl;
return 0; //main結束後,全局對象a消亡,調用析構函數
}
//輸出結果爲:
//destructor
//destructor
//end main
//destructor
相關實例
析構函數和構造函數被調用實例:
class A:
{
public:
int a;
A(int i){ //構造函數,也可算類型轉換構造函數
a=i;
cout << "a" << a << " construct" << endl;
};
~ A(){
cout << "a"<< a << " destruct" << endl;
}
};
A a1(1); //step1:全局對象在main之前初始化,在整個程序結束後消亡
void func(){
static A a2(2); //step8:靜態局部變量初始化,在全部程序結束後消亡
A a3(3); //step9:局部變量a3初始化,在func結束後消亡
cout << "func end" << endl;
}
int main(){
A a4(4); //step2:初始化a4,在main結束後消亡
a4 = 6; //step3:類型轉換構造函數生成值爲6的臨時對象
//step4:臨時對象賦值語句結束後消亡
cout << "main" << endl; //step5
{A a5(5);} //step6:局部對象a5生成
//step7:包含a5的最內層大括號結束後a5消亡
func();
cout << "end main" << endl;
}
//輸出結果
// a1 construct
// a2 construct
// a4 construct
// a6 construct
// a6 destruct
// main
// a5 construct
// a5 destruct
// a2 construct
// a3 construct
// func end
// a3 destruct
// end main
// a6 destruct //先消亡main內的
// a2 destruct //因爲a1比a2先初始化,後消亡
// a1 destruct
構造函數在不同編譯器中表現
dev c++呼籲優化目的沒有生成返回值臨時對象,一般情況下沒什麼問題