類默認生成的六個成員函數
一、構造函數
我們知道,類的數據成員是不能在聲明類的時候初始化的,因爲類並不是一個實體,而是一種抽象的數據類型,並不佔據存儲空間。爲了解決這個問題,C++提供了構造函數來處理對象的初始化。
1、構造函數的作用
構造函數是一種特殊的成員函數,與其他成員函數不同,構造函數是在對象被實例化的時候自動被調用的,而且只執行這一次,它不能被用戶調用。構造函數沒有this指針。
構造函數的名字是固定的,與類名相同,不能由用戶任意命名,它沒有類型,沒有返回值。
構造函數的功能是由用戶自己定義的,用戶根據初始化的要求設計函數體和函數參數。
如果用戶自己沒有定義構造函數,則C++系統會自動生成一個構造函數,稱作默認構造函數或者缺省構造函數。只是這個構造函數是空的,不執行任何操作,但還是會被調用。
例:設計一個日期類,並定義構造函數
- classdate
- {
- public:
- date()
- {
- _year = 0;
- _month = 0;
- _day = 0;
- }
- private:
- int _year;
- int _month;
- int _day;
- };
classdate
{
public:
date() //構造函數
{
_year = 0; //初始化爲0
_month = 0;
_day = 0;
}
private:
int _year; //一般數據成員以_開頭或者以m_開頭
int _month;
int _day;
};
2、帶參的構造函數
有時候用戶希望對不同的對象賦予不同的初始值,這時就必須用到帶參的構造函數,實現不同的初始化。其對應的實參是在定義對象的時候給定的。
構造函數首部的一般形式:
構造函數名(類型1 形參1,類型2 形參2,...)
因爲用戶不能調用構造函數,所以實參是在定義對象的時候給出的:
類名 對象名(實參1,實參2,...);
例:
- classdate
- {
- public:
- date(int year ,intmonth,intday)
- {
- _year =year;
- _month =month;
- _day =day;
- }
- private:
- int _year;
- int _month;
- int _day;
- };
- intmain()
- {
- date d1(2016, 7, 19);
- system("pause");
- return 0;
- }
classdate
{
public:
date(int year ,intmonth,intday) //構造函數
{
_year =year;
_month =month;
_day =day;
}
private:
int _year; //一般數據成員以_開頭或者以m_開頭
int _month;
int _day;
};
intmain()
{
date d1(2016, 7, 19); //定義一個對象,它有初始值
system("pause");
return 0;
}
3、用參數列表對數據成員初始化
C++還提供了一種初始化數據成員的方法——參數初始化表。這種方法不再函數體內對數據成員初始化,而是在函數首部實現。
例:
- classdate
- {
- public:
- date(int year , int month,int day ) :_year(year)
- , _month(month)
- , _day(day)
- {
- }
- private:
- int _year;
- int _month;
- int _day;
- };
classdate
{
public:
date(int year , int month,int day ) :_year(year)
, _month(month)
, _day(day)
{
}
private:
int _year; //一般數據成員以_開頭或者以m_開頭
int _month;
int _day;
};
即在原來函數首部的末尾加上一個冒號,然後列出參數的初始化表,並且參數之間以逗號隔開。
爲什麼會有這種方法呢???
這是因爲有些數據成員比如,引用,const修飾的變量必須在創建的時候初始化,所以只能通過初始化列表來初始化。而且這種方法比較高效。
4、構造函數可重載
儘管構造函數可以有多個,但是對於每一個對象來說,建立對象時只執行其中一個構造函數。還有一點需要強調,如果聲明一個無參的對象應該寫成
date d1;
而不應該寫成
date d1(); //這是一個函數聲明,類型是date,函數名是d1,參數爲空
5、使用默認參數的構造函數
構造函數中參數的值既可以通過實參傳遞,也可以指定爲某些默認值,如果用戶不指定實參值,編譯系統就使形參取默認值。最好在聲明構造函數時指定默認值,而不能只在定義構造函數時指定默認值。
在一個類中定義了全部是默認參數的構造函數後,不能再定義重載構造函數。
二、拷貝構造函數
1、拷貝構造函數就是用一個已有的對象複製出多個完全相同的對象。
例:date d2(d1); 其作用就是用已有的對象d1去創建出一個新對象d2,並且d1與d2完全相同。
拷貝構造函數也是構造函數,但他只有一個參數(這個參數只能是本類的一個對象),而且採用對象的常引用形式。拷貝構造函數的作用就是將實參對象的各成員值一一賦給新的對象中對應的成員。
例:
- classdate
- {
- public:
- date(int year , int month,int day ) :_year(year)
- , _month(month)
- , _day(day)
- {
- }
- date(const date &d)
- {
- _year =d._year;
- _month =d._month;
- _day =d._day;
- }
- private:
- int _year;
- int _month;
- int _day;
- };
classdate
{
public:
date(int year , int month,int day ) :_year(year)
, _month(month)
, _day(day)
{
}
date(const date &d) //拷貝構造函數
{
_year =d._year;
_month =d._month;
_day =d._day;
}
private:
int _year; //一般數據成員以_開頭或者以m_開頭
int _month;
int _day;
};
注意:拷貝構造函數是從無到有的創建一個新對象。
拷貝構造函數的參數只能是本類對象的引用,如果不是引用的話會引發無窮的遞歸。
拷貝構造函數是類默認生成的,但是當牽扯到動態內存的時候默認生成的拷貝構造函數就不能滿足我們的要求,這時需 要我們自己根據需要定義一個拷貝構造函數。
2、什麼時候調用拷貝構造函數?
程序中需要建立一個新的對象,並且用另一個同類的對象初始化它。
當函數的參數爲類的對象時。在調用函數時需要將實參對象完整的傳遞給形參,也就是需要建立一個實參的拷貝。
函數的返回值是類的對象。在函數調用完畢將返回值帶回函數調用處時,此時需要將函數中的對象複製一個臨時對象返回
三、析構函數
析構函數也是一個特殊的成員函數,它的作用與構造函數相反,它的名字是類名前面加取反“~”符號。它是在對象釋放前系統自動調用的。
1、析構函數的作用並不是刪除對象,而是在撤銷對象時做一些清理工作。比如關閉打開的文件,釋放開闢的動態內存等工作。
2、析構函數不返回任何值,沒有函數類型,沒有參數,因此也不能重載。
3、調用構造函數和析構函數的順序
因爲函數壓棧的關係,所以先構造的後析構,後構造的先析構。如果有全局對象或者靜態局部對象,則它們在main函數結束或者調用exit函數時2被析構。
四、賦值運算符重載
1、如果已經定義了兩個或多個對象,則這些同類的對象之間可以相互賦值,即一個對象的值可以賦給另一個同類的對象。這裏所指的對象的值是對象中所有數據成員的值。
對象之間的賦值是通過賦值運算符“=”重載實現的,即將一個對象的值一一複製給另一對象的對應成員。
如:
- classdate
- {
- public:
- date(int year , int month,int day ) :_year(year)
- , _month(month)
- , _day(day)
- {
- }
- date operator=(date&d)
- {
- _year =d._year;
- _month =d._month;
- _day =d._day;
- }
- private:
- int _year;
- int _month;
- int _day;
- };
classdate
{
public:
date(int year , int month,int day ) :_year(year)
, _month(month)
, _day(day)
{
}
date operator=(date&d) //複製運算符重載
{
_year =d._year;
_month =d._month;
_day =d._day;
}
private:
int _year; //一般數據成員以_開頭或者以m_開頭
int _month;
int _day;
};
對象賦值的一般形式:
對象名1=對象名2;
2、賦值運算符賦值時,類的數據成員中不能包括動態分配的數據,否則在析構的時候會將同一塊內存釋放多次,產生錯誤。
3、要區分賦值運算符重載和拷貝構造函數的功能。賦值運算符是對一個已經創建的對象賦值。
五、取地址運算符重載和const修飾的取地址運算符重載
這兩個默認的成員函數一般不用重新定義,
例:
- classdate
- {
- public:
-
- const date * operator&()const
- {
- return this ;
- }
- private:
- int _year;
- int _month;
- }
classdate
{
public:
const date * operator&()const
{
return this ;
}
private:
int _year;
int _month;
}