1. 程式人生 > >總結c++類的建構函式 拷貝建構函式 解構函式 賦值運算子過載的特點以及函式呼叫順序

總結c++類的建構函式 拷貝建構函式 解構函式 賦值運算子過載的特點以及函式呼叫順序

對 c++類的建構函式 拷貝建構函式 解構函式 賦值運算子過載 相關知識的總結,並附上例子,希望對大家有幫助,有錯誤大家可以指出來

 一 建構函式  
 1 建構函式: 建構函式時一個特殊的成員函式,用來初始化物件的資料成員,在物件建立時,由編譯器自動呼叫,在物件的生命週期且只調用一次。
  定義建構函式的原型的格式: 類名(形參列表);   
   在類外定義建構函式的格式:類名::類名(形參列表)  {} 

class Date        //date.h
{
public:
	Date(int year, int month, int day);
private:
	int _year;
	int _month;
	int _day;
};
Date::Date(int year,int month,int day)  //date.cpp
{
	// 函式語句
}

2 建構函式的特點:

    (1)函式名與類名相同;

    (2)沒有返回值,也不能指定為void型別

    (3)有初始化列表 

public:
	Date(int year = 1995, int month = 12, int day = 8)
		:_year(year)
		,_month(month)
		, _day(day)
	{
	}
       初始化順序:(1)每個成員在初始化列表中只能出現一次 

                  (2)資料成員在類中定義的順序就是引數列表中的初始化順序

        類中以下成員必須放在初始化列表中初始化:

         (a)引用資料成員 (b)  const 資料成員

 (c)基類沒有顯示的建構函式,必須在派生類的建構函式中初始化基類建構函式

        (d)類型別成員(該類沒有預設的建構函式)  因為初始化不必呼叫預設建構函式來初始化,直接呼叫拷貝建構函式

class Food
{
public:
	Food(int q = 10)
	{
		_quantity = q;
		cout << "food 構造" << endl;
	}
	Food(const Food& f)
	{
		_quantity = f._quantity;
		cout << "food 拷貝構造" << endl;
	}
private:
	int _quantity;
};


(4)建構函式可以過載,預設引數只能在原型宣告中指定,不能在函式定義中指定。

(5)無參建構函式和帶預設的建構函式都認為是預設建構函式,並且預設建構函式只能有一個

(6)建構函式不能用const 修飾

 3 建構函式的作用 :構建物件,初始化物件,型別轉換 

// 型別轉換建構函式,根據一個指定的型別的物件建立一個本類的物件
        // 例如:下面將根據一個double型別的物件建立了一個Complex物件
        Complex::Complex(double r)
        {
                m_real = r;
                m_imag = 0.0;
        }

二 拷貝建構函式(特殊的建構函式)

1  只有單個形參,而且該形參是對本類型別物件的引用(常用const修飾),建立物件時使用已存在的同類物件來進行初始化,由編譯器自動呼叫

  2 特徵:(1)是建構函式的過載

     (2)它的引數必須是同類型別物件的引用。原因:防止拷貝建構函式無限遞迴下去

     (3)如果沒有顯示定義,系統會自動合成一個預設的拷貝建構函式。預設的拷貝建構函式會依次拷貝類的資料                   成員完成初始化。

  3 使用場景:

class Date
{
public:
	Date(int year = 1995,int month = 12,int day = 8)  // 建構函式
		:_year(year)
		, _month(month)
		, _day(day)
	{
		cout << "date()建構函式"<< this << endl;
	}
	Date(const Date& d)//  拷貝建構函式
		:_year(d._year)
		, _month(d._month)
		, _day(d._day)
	{
		cout << "date(&d)" << this << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

    (1)物件例項化

Date t(1996,11,7);
Date t1(t);  // 呼叫拷貝建構函式
Date t2 = t; // 呼叫拷貝建構函式  此處先不考慮賦值操作符過載

    (2)傳值方式作為函式的引數

void Test(const Date d)  //傳值方式作為函式的引數  呼叫拷貝建構函式
{
	return;
}

    (3)傳值方式作為返回值

Date test()   
{
	Date date;  // 呼叫建構函式
	return date;// 值傳遞作為函式返回值   呼叫拷貝建構函式
	//return Date();  //  返回無名物件不掉用拷貝建構函式 只調用建構函式
}

Date& FunTest(Date& d)函式呼叫順序


Date FunTest(Date d)函式呼叫順序


Date FunTest(Date d)函式 返回值是無名物件呼叫順序


私有的拷貝建構函式:防止一個物件不被通過傳值方式傳遞。

<pre name="code" class="cpp">class A
{
	…
private:
	A(const A &a); // 私有的拷貝建構函式
};
A fun(A a){}

A  a;
A b(a);  // 錯誤,呼叫私有的拷貝建構函式
b = fun(a); // 錯誤 ,同上

解構函式

 解構函式:與建構函式的功能相反,在物件銷燬時,由編譯器自動呼叫,完成類的一些資源清理和汕尾工作。

注意:解構函式的目的是在系統回收物件 記憶體之前執行結束清理工作,以便記憶體可被重新用於儲存新物件。

class CArray
{
public:
	CArray(size_t capacity) //建構函式
		:_capacity(capacity)
	{
		_pData = (int *)malloc(capacity * sizeof(int));
		_size = 0;
	}
	~CArray() //解構函式
	{
		if (NULL != _pData)
		{
			free(_pData);
			_pData = NULL;
		}
		_size = 0;
		_capacity = 0;
	}
private:
	int *_pData;
	size_t _size;
	size_t _capacity;
};

2 特性 

a 解構函式沒有引數沒有返回值,函式名是類名前加上`.

b 一個類有且只有一個解構函式。若未顯示定義,系統會自動生成預設的解構函式。

c解構函式並不是刪除物件,只是做一些清理工作

d 呼叫次序與建構函式相反,最先構造的物件最後被析構。

四 賦值運算子過載

c++中,對於任何一個類,如果沒有使用者自定義的賦值運算子函式,系統會自動的生成一個預設的(預設的完成的資料成員的逐位複製)。特殊情況下:如類中有指標形式就不能直接相互賦值,不然可能造成指標懸掛問題。

注意:(1)賦值運算子不能過載為友元函式,只能過載為一個非靜態成員函式。

   (2)賦值運算子過載函式不能被繼承。

Date& Date::operator=(const Date& d) // 賦值運算子過載 
{
	cout << "operator= 賦值運算子過載 " << this << endl;
	if (this != &d) // 判斷是否自己對自己賦值
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	return *this;
}

注意 在賦值函式為:Date operator=(const Date& d)的情況下

void funTestOperator()
{
	Date d1(2016, 10, 16);// d1 呼叫建構函式
	Date d2(2016, 10, 17);// d2 呼叫建構函式
	Date d3(2016, 10, 18); // d3呼叫建構函式
	d1 = d2 = d3;  // 相當於d1.operator=(d2.operator=(d3)) 
	// d2.operator=(d3) 呼叫一次賦值運算子函式,返回值1呼叫拷貝建構函式
	// d1.operator=(返回值1) 呼叫第二次賦值運算子函式,返回值2呼叫拷貝建構函式
	// 返回值2 呼叫解構函式 返回值d1 呼叫解構函式
}
// 函式結束呼叫d3 d2 d1 的解構函式

 2 在賦值函式為:Date& operator=(const Date&d)的情況下

void funTestOperator()
{
	Date d1(2016, 10, 16);// d1呼叫建構函式
	Date d2(2016, 10, 17);// d2呼叫建構函式
	Date d3(2016, 10, 18);// d3呼叫建構函式
	d1 = d2 = d3; // 相當於d1.operator=(d2.operator=(d3)) 
	// 呼叫兩次賦值操作符函式
}

不同的是沒有臨時物件的產生,因為operator=返回的是對當前物件的引用,而引用只是別名,而不是構造新物件的 

區別拷貝建構函式和賦值操作符過載函式的呼叫區別

    Date d1,d3;
    Date d2 = d1; // 拷貝建構函式
    d3 = d1;  //賦值操作符過載函式

有什麼問題,大家可以評論。