C++中構造函數與析構函數的調用順序

構造函數、析構函數與拷貝構造函數介紹

2.1構造函數

  • 構造函數不能有返回值
  • 缺省構造函數時,系統將自動調用該缺省構造函數初始化對象,缺省構造函數會將全部數據成員都初始化爲零或空 
  • 建立一個對象時,系統自動調用構造函數

2.2析構函數

  • 析構函數沒有參數,也沒有返回值。不能重載,也就是說,一個類中只可能定義一個析構函數
  • 若是一個類中沒有定義析構函數,系統也會自動生成一個默認的析構函數,爲空函數,什麼都不作
  • 調用條件:1.在函數體內定義的對象,當函數執行結束時,該對象所在類的析構函數會被自動調用;2.用new運算符動態構建的對象,在使用delete運算符釋放它時。

2.3拷貝構造函數

拷貝構造函數實際上也是構造函數,具備通常構造函數的全部特性,其名字也與所屬類名相同。拷貝構造函數中只有一個參數,這個參數是對某個同類對象的引用。它在三種狀況下被調用:
  • 用類的一個已知的對象去初始化該類的另外一個對象時;
  • 函數的形參是類的對象,調用函數進行形參和實參的結合時;
  • 函數的返回值是類的對象,函數執行完返回調用者。

3.構造函數與析構函數的調用順序

對象是由「底層向上」開始構造的,當創建一個對象時,首先調用基類的構造函數,而後調用下一個派生類的構造函數,依次類推,直至到達派生類次數最多的派生次數最多的類的構造函數爲止。由於,構造函數一開始構造時,老是要調用它的基類的構造函數,而後纔開始執行其構造函數體,調用直接基類構造函數時,若是無專門說明,就調用直接基類的默認構造函數。在對象析構時,其順序正好相反。 ios

#include<iostream>
using namespace std;
class point
{
private:
	int x,y;//數據成員
public:
	point(int xx=0,int yy=0)//構造函數
	{
		x=xx;
		y=yy;
		cout<<"構造函數被調用"<<endl;
	}
	point(point &p);//拷貝構造函數,參數是對象的引用
	~point(){cout<<"析構函數被調用"<<endl;}
	int get_x(){return x;}//方法
	int get_y(){return y;}
};

point::point(point &p)
{
	x=p.x;//將對象p的變相賦值給當前成員變量。
	y=p.y;
	cout<<"拷貝構造函數被調用"<<endl;
}

void f(point p)
{
	cout<<p.get_x()<<"	"<<p.get_y()<<endl;
}

point g()//返回類型是point
{
	point a(7,33);
	return a;
}

void main()
{
	point a(15,22);
	point b(a);//構造一個對象,使用拷貝構造函數。
	cout<<b.get_x()<<"	"<<b.get_y()<<endl;
	f(b);
	b=g();
	cout<<b.get_x()<<"	"<<b.get_y()<<endl;
}

4.3結果解析
構造函數被調用  //point a(15,22);
拷貝構造函數被調用 //point b(a);拷貝構造函數的第一種調用狀況: 用類的一個已知的對象去初始化該類的另外一個對象時
15 22 //cout<<b.get_x()<<" "<<b.get_y()<<endl;
拷貝構造函數被調用 //f(b);拷貝構造函數的第二種調用狀況: 函數的形參是類的對象,調用函數進行形參和實參的結合時
15 22  //void f(point p)函數輸出對象b的成員
析構函數被調用 //f(b);析構函數的第一種調用狀況: 在函數體內定義的對象,當函數執行結束時,該對象所在類的析構函數會被自動調用
構造函數被調用 //b=g();的函數體內point a(7,33);建立對象a
拷貝構造函數被調用 //b=g();拷貝構造函數的第三種調用狀況,拷貝a的值賦給b: 函數的返回值是類的對象,函數執行完返回調用者
析構函數被調用 //拷貝構造函數對應的析構函數
析構函數被調用 //b=g();的函數體內對象a析構
7 33
析構函數被調用 //主函數體b對象的析構
析構函數被調用 //主函數體a對象的析構

5.實例2

#include <iostream>
using namespace std;
//基類
class CPerson
{
	char *name;        //姓名
	int age;            //年齡
	char *add;        //地址
public:
	CPerson(){cout<<"constructor - CPerson! "<<endl;}
	~CPerson(){cout<<"deconstructor - CPerson! "<<endl;}
};

//派生類(學生類)
class CStudent : public CPerson
{
	char *depart;    //學生所在的系
	int grade;        //年級
public:
	CStudent(){cout<<"constructor - CStudent! "<<endl;}
	~CStudent(){cout<<"deconstructor - CStudent! "<<endl;}
};

//派生類(教師類)
//class CTeacher : public CPerson//繼承CPerson類,兩層結構
class CTeacher : public CStudent//繼承CStudent類,三層結構
{
	char *major;    //教師專業
	float salary;    //教師的工資
public:
	CTeacher(){cout<<"constructor - CTeacher! "<<endl;}
	~CTeacher(){cout<<"deconstructor - CTeacher! "<<endl;}
};

//實驗主程序
void main()
{
	//CPerson person;
	//CStudent student;
	CTeacher teacher;
}
 

5.2運行結果


5.3說明

在實例2中,CPerson是CStudent的父類,而CStudent又是CTeacher的父類,那麼在建立CTeacher對象的時候,首先調用基類也就是CPerson的構造函數,而後按照層級,一層一層下來。
相關文章
相關標籤/搜索