1. 程式人生 > 實用技巧 >C++物件的構造和析構

C++物件的構造和析構

前言
建立一個物件時,常常需要作某些初始化的工作,例如對資料成員賦初值。注意,類的資料成員是不能在宣告類時初始化的。
為了解決這個問題,C++編譯器提供了建構函式(constructor)來處理物件的初始化。建構函式是一種特殊的成員函式,與其他成員函式不同,不需要使用者來呼叫它,而是在建立物件時自動執行。
3.1構造和解構函式
1建構函式和解構函式的概念
有關建構函式
1建構函式定義及呼叫
1)C++中的類可以定義與類名相同的特殊成員函式,這種與類名相同的成員函式叫做建構函式;
2)建構函式在定義時可以有引數;
3)沒有任何返回型別的宣告。
2建構函式的呼叫
自動呼叫:一般情況下C++編譯器會自動呼叫建構函式

手動呼叫:在一些情況下則需要手工呼叫建構函式

有關解構函式
3)解構函式定義及呼叫
1)C++中的類可以定義一個特殊的成員函式清理物件,這個特殊的成員函式叫做解構函式
語法:~ClassName()
2)解構函式沒有引數也沒有任何返回型別的宣告
3)解構函式在物件銷燬時自動被呼叫
4)解構函式呼叫機制
C++編譯器自動呼叫
程式碼演示:dm01_建構函式的基礎.cpp
2 C++編譯器構造析構方案 PK 物件顯示初始化方案
設計建構函式和解構函式的原因
面向物件的思想是從生活中來,手機、車出廠時,是一樣的。
生活中存在的物件都是被初始化後才上市的;初始狀態是物件普遍存在的一個狀態的
普通方案:

為每個類都提供一個public的initialize函式;
物件建立後立即呼叫initialize函式進行初始化。
優缺點分析
1)initialize只是一個普通的函式,必須顯示的呼叫
2)一旦由於失誤的原因,物件沒有初始化,那麼結果將是不確定的
沒有初始化的物件,其內部成員變數的值是不定的
3)不能完全解決問題
//為什麼物件需要初始化 有什麼樣的初始化方案
#include “iostream”
using namespace std;

/*
思考為什麼需要初始化
面向物件思想來自生活,手機、車、電子產品,出廠時有初始化
怎麼樣進行初始化?

方案1:顯示呼叫方法
缺點:易忘、麻煩;顯示呼叫init,不能完全解決問題

*/
class Test21
{
public:
int m;
int getM() const { return m; }
void setM(int val) { m = val; }
int n;
int getN() const { return n; }
void setN(int val) { n = val; }

public:
int init(int m,int n)
{
this->m = m;
this->n = n;
return 0;
}
protected:
private:
};

int main()
{
int rv =0;
Test21 t1; //無參建構函式的呼叫方法
Test21 t2;
//t1.init(100, 200);
//t2.init(300, 400);
cout<<t1.getM()<<" “<<t1.getN()<<endl;
cout<<t2.getM()<<” "<<t2.getN()<<endl;
//定義物件陣列時,沒有機會進行顯示初始化
Test21 arr[3];
//Test arr_2[3] = {Test(1,3), Test(), Test()};
system(“pause”);
return rv;
}
3.2建構函式的分類及呼叫
C++編譯器給程式設計師提供的物件初始化方案,高階大氣上檔次。
//有引數建構函式的三種呼叫方法
class Test
{
private:
int a;
int b;

public:
//無引數建構函式
Test()
{
;
}
//帶引數的建構函式
Test(int a, int b)
{
;
}
//賦值建構函式
Test(const Test &obj)
{
;
}

public:
void init(int _a, int _b)
{
a = _a;
b = _b;
}
};
1無引數建構函式
呼叫方法: Test t1, t2;
2有參建構函式
有參建構函式的三種呼叫方法
//有引數建構函式的三種呼叫方法
class Test5
{
private:
int a;
public:
//帶引數的建構函式
Test5(int a)
{
printf("\na:%d", a);
}
Test5(int a, int b)
{
printf("\na:%d b:%d", a, b);
}
public:
};

int main55()
{
Test5 t1(10); //c++編譯器預設呼叫有參建構函式 括號法
Test5 t2 = (20, 10); //c++編譯器預設呼叫有參建構函式 等號法
Test5 t3 = Test5(30); //程式設計師手工呼叫建構函式 產生了一個物件 直接呼叫構造建構函式法
system(“pause”);
return 0;
}
3拷貝建構函式呼叫時機
賦值建構函式的四種呼叫場景(呼叫時機)
第1和第2個呼叫場景
#include “iostream”
using namespace std;

class AA
{
public:
AA() //無參建構函式 預設建構函式
{
cout<<“我是建構函式,自動被呼叫了”<<endl;
}
AA(int _a) //無參建構函式 預設建構函式
{
a = _a;
}
AA(const AA &obj2)
{
cout<<“我也是建構函式,我是通過另外一個物件obj2,來初始化我自己”<<endl;
a = obj2.a + 10;
}
~AA()
{
cout<<“我是解構函式,自動被呼叫了”<<endl;
}
void getA()
{
printf(“a:%d \n”, a);
}
protected:
private:
int a;
};
//單獨搭建一個舞臺
void ObjPlay01()
{
AA a1; //變數定義
//賦值建構函式的第一個應用場景
//用物件1 初始化 物件2
AA a2 = a1; //定義變數並初始化 //初始化法
a2 = a1; //用a1來=號給a2 編譯器給我們提供的淺copy
}

第2個應用場景
//單獨搭建一個舞臺
void ObjPlay02()
{
AA a1(10); //變數定義
//賦值建構函式的第一個應用場景
//用物件1 初始化 物件2
AA a2(a1); //定義變數並初始化 //括號法
//a2 = a1; //用a1來=號給a2 編譯器給我們提供的淺copy
a2.getA();
}
//注意:初始化操作 和 等號操作 是兩個不同的概念

第3個呼叫場景
#include “iostream”
using namespace std;

class Location
{
public:
Location( int xx = 0 , int yy = 0 )
{
X = xx ; Y = yy ; cout << “Constructor Object.\n” ;
}
Location( const Location & p ) //拷貝建構函式
{
X = p.X ; Y = p.Y ; cout << “Copy_constructor called.” << endl ;
}
~Location()
{
cout << X << “,” << Y << " Object destroyed." << endl ;
}
int GetX () { return X ; } int GetY () { return Y ; }
private : int X , Y ;
} ;

//alt + f8 排版
void f ( Location p )
{
cout << “Funtion:” << p.GetX() << “,” << p.GetY() << endl ;
}

void mainobjplay()
{
Location A ( 1, 2 ) ; //形參是一個元素,函式呼叫,會執行實參變數初始化形參變數
f ( A ) ;
}

void main()
{
mainobjplay();
system(“pause”);
}

第4個呼叫場景
第四個應用場景
#include “iostream”
using namespace std;
class Location
{
public:
Location( int xx = 0 , int yy = 0 )
{
X = xx ; Y = yy ; cout << “Constructor Object.\n” ;
}
Location( const Location & p ) //複製建構函式
{
X = p.X ; Y = p.Y ; cout << “Copy_constructor called.” << endl ;
}
~Location()
{
cout << X << “,” << Y << " Object destroyed." << endl ;
}
int GetX () { return X ; } int GetY () { return Y ; }
private : int X , Y ;
} ;

//alt + f8 排版
void f ( Location p )
{
cout << “Funtion:” << p.GetX() << “,” << p.GetY() << endl ;
}

Location g()
{
Location A(1, 2);
return A;
}

//物件初始化操作 和 =等號操作 是兩個不同的概念
//匿名物件的去和留,關鍵看,返回時如何接
void mainobjplay()
{
//若返回的匿名物件,賦值給另外一個同類型的物件,那麼匿名物件會被析構
//Location B;
//B = g(); //用匿名物件 賦值 給B物件,然後匿名物件析構
//若返回的匿名物件,來初始化另外一個同類型的物件,那麼匿名物件會直接轉成新的物件
Location B = g();
cout<<“傳智掃地僧測試”<<endl;
}

void main()
{
mainobjplay();
system(“pause”);
}
在這裡插入圖片描述
4預設建構函式
二個特殊的建構函式
1)預設無參建構函式
當類中沒有定義建構函式時,編譯器預設提供一個無參建構函式,並且其函式體為空
2)預設拷貝建構函式
當類中沒有定義拷貝建構函式時,編譯器預設提供一個預設拷貝建構函式,簡單的進行成員變數的值複製
3.3建構函式呼叫規則研究
1)當類中沒有定義任何一個建構函式時,c++編譯器會提供預設無參建構函式和預設拷貝建構函式
2)當類中定義了拷貝建構函式時,c++編譯器不會提供無引數建構函式
3) 當類中定義了任意的非拷貝建構函式(即:當類中提供了有參建構函式或無參建構函式),c++編譯器不會提供預設無參建構函式
4 )預設拷貝建構函式成員變數簡單賦值
總結:只要你寫了建構函式,那麼你必須用。

構造析構階段性總結
1)建構函式是C++中用於初始化物件狀態的特殊函式
2)建構函式在物件建立時自動被呼叫
3)建構函式和普通成員函式都遵循過載規則
4)拷貝建構函式是物件正確初始化的重要保證
5)必要的時候,必須手工編寫拷貝建構函式
========》1個物件的初始化講完了,增加一個案例。