總結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;
}
注意 1 在賦值函式為: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; //賦值操作符過載函式
有什麼問題,大家可以評論。