一、構造函數
構造函數是特殊的成員函數,需要注意的是,構造函數名稱雖然叫構造函數,但是需要注意的是它並不是開闢空間創建對象,而是對對象進行初始化。如果類中沒有顯示定義構造函數,C++編譯器會自動生成一個無參的構造函數,一旦用戶顯示定義編譯器將不再生成。
class Date{
public:
//無參的構造函數
Date(){
}
//帶參的構造函數
Date(int year,int mouth,int day){
_year = year;
_mouth = mouth;
_day = day;
}
private:
int _year;
int _mouth;
int _day;
};
構造函數的特徵:
- 函數名與類名相同;
- 無返回值;
- 對象實例化時編譯器自動調用對應的構造函數;
- 構造函數可以重載;
無參的構造函數和全缺省的構造函數都被稱爲默認構造函數,並且默認構造函數只能有一個。注意:無參構造函數、全缺省構造函數、編譯器默認生成的默認構造函數都可以被認爲是默認構造函數。
例如下面的代碼,有兩個默認的構造函數,再編譯過程中,編譯器會報錯。
class Date {
public:
Date() {
_year = 1900;
_mouth = 1;
_day = 1;
}
Date(int year = 1900,int mouth = 1,int day = 1) {
_year = year;
_mouth = mouth;
_day = day;
}
private:
int _year;
int _mouth;
int _day;
};
int main() {
Date d1;
return 0;
}
那麼問題來了,編譯器默認生成的構造函數到底有什麼用?
答案是:編譯器生成默認的構造函數會對自定類型(是用class、struct、union自己定義的類型)成員調用;
class Time{
public:
Time(){
cout << "Time()" << endl;
_hout = 0;
_minute = 0;
_second = 0;
}
private:
int _hour;
int _minute;
int _second;
};
class Date{
private:
//基本類型(內置類型)
int _year;
int _mouth;
int _day;
//自定義類型
Time _t;
};
二、析構函數
析構函數:與構造函數的功能相反,構造函數不是完成對象的銷燬,局部對象的銷燬工作是由編譯器完成的,而對象在銷燬時會自動調用析構函數,完成類的一些清理工作。
typedef int DataType;
class SeqList{
public :
SeqList (int capacity = 10){
_pData = (DataType*)malloc(capacity * sizeof(DataType));
assert(_pData);
_size = 0;
_capacity = capacity;
}
~SeqList(){
if (_pData){
free(_pData ); // 釋放堆上的空間
_pData = NULL; // 將指針置爲空
_capacity = 0;
_size = 0;
}
}
private :
int* _pData ;
size_t _size;
size_t _capacity;
};
析構函數的特性:
- 析構函數名是在類名前面加上字符 ~ ;
- 無參數、無返回值;
- 一個類有且只有一個析構函數。若未顯示定義,系統會自動生成默認的析構函數;
- 對象生命週期結束時,C++編譯系統自動調用析構函數;
對於編譯器自動生成的默認析構函數,類似於構造函數,會對自定義類型成員調用它的析構函數;
三、拷貝構造函數
拷貝構造函數:只有單個形參,該形參時對被類類型對象的引用(一般常用const修飾),在用已存在的類類型對象創建新對象時由編譯器自動調用。
注意:拷貝構造函數只是構造函數的一種重載形式!拷貝構造函數的參數有且只有一個!!必須使用引用傳參!!,使用傳值的方式會引發無窮遞歸引用。
class Date {
public:
//構造函數,初始化列表
Date(int year, int mouth, int day)
:_year(year)
, _mouth(mouth)
, _day(day)
{
cout << "Date()" << endl;
}
//拷貝構造函數
Date(const Date& d) {
_year = d._year;
_mouth = d._mouth;
_day = d._day;
}
//析構函數
~Date() {
cout << "~Date()" << endl;
}
private:
int _year;
int _mouth;
int _day;
};
同樣的,如果沒有顯示定義,系統將會生成默認的拷貝構造函數,但是默認的構造拷貝函數時按照內存存儲的字節序完成拷貝,這種拷貝我們稱爲淺拷貝(值拷貝)。
四、賦值操作符重載
C++爲了增強代碼的可讀性引入了運算符重載,運算符重載是具有特殊函數名的函數,也具有其返回值類型,函數名字以及參數列表,其返回值類型與參數列表與普通函數類似;
函數名字:關鍵字operator+需要重載的運算符符號;
函數原型:返回值類型operator操作符(參數列表);
注意:
- 不能通過連接其他符號來創建新的操作符:比如operator@
- 重載操作符必須有一個類類型或者枚舉類型的操作數
- 用於內置類型的操作符,其含義不能改變,例如:內置的整型+,不 能改變其含義 作爲類成員的重載函數時,其形參看起來比操作數數目少1成員函數的 操作符有一個默認的形參this,限定爲第一個形參
- .* 、:: 、sizeof 、?: 、. 注意以上5個運算符不能重載!!!
class Date {
public:
/*
Date(int year,int mouth,int day){
_year = year;
_mouth = mouth;
_day = day;
}
*/
//構造函數,初始化列表
Date(int year, int mouth, int day)
:_year(year)
, _mouth(mouth)
, _day(day)
{
cout << "Date()" << endl;
}
//拷貝構造函數
Date(const Date& d) {
_year = d._year;
_mouth = d._mouth;
_day = d._day;
}
//賦值運算符重載
bool operator=(const Date& d) {
if (this != &d) {
_year = d._year;
_mouth = d._mouth;
_day = d._day;
}
}
//運算符重載
bool operator==(const Date& d) {
return _year == d._year
&& _mouth == d._mouth
&& _day == d._day;
}
//析構函數
~Date() {
cout << "~Date()" << endl;
}
private:
int _year;
int _mouth;
int _day;
};
五、const成員
const修飾的類成員函數稱之爲 const 成員函數,const修飾類成員函數,實際修飾該成員函數隱含的 this 指針,表明在該成員函數中不能對類的任何成員進行修改;
class Date{
public :
void Display (){
cout<<"Display ()" <<endl;
cout<<"year:" <<_year<< endl;
cout<<"month:" <<_month<< endl;
cout<<"day:" <<_day<< endl<<endl ;
}
void Display () const{
cout<<"Display () const" <<endl;
cout<<"year:" <<_year<< endl;
cout<<"month:" <<_month<< endl;
cout<<"day:" <<_day<< endl<<endl;
}
private :
int _year ; // 年
int _month ; // 月
int _day ; // 日
};
void Test (){
Date d1 ;
d1.Display ();
const Date d2;
d2.Display ();
}
六、取地址及const取地址操作符重載
這兩個函數一般不用重新定義,編譯器會默認生成;
class Date{
public :
Date* operator&(){
return this ;
}
const Date* operator&()const{
return this ;
}
private :
int _year ; // 年
int _month ; // 月
int _day ; // 日
}