【C++自學筆記】類的六個默認成員函數

一、構造函數

構造函數是特殊的成員函數,需要注意的是,構造函數名稱雖然叫構造函數,但是需要注意的是它並不是開闢空間創建對象,而是對對象進行初始化。如果類中沒有顯示定義構造函數,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;
};

構造函數的特徵:

  1. 函數名與類名相同;
  2. 無返回值;
  3. 對象實例化時編譯器自動調用對應的構造函數;
  4. 構造函數可以重載;

無參的構造函數和全缺省的構造函數都被稱爲默認構造函數,並且默認構造函數只能有一個。注意:無參構造函數、全缺省構造函數、編譯器默認生成的默認構造函數都可以被認爲是默認構造函數。

例如下面的代碼,有兩個默認的構造函數,再編譯過程中,編譯器會報錯。

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;
};

析構函數的特性:

  1. 析構函數名是在類名前面加上字符 ~ ;
  2. 無參數、無返回值;
  3. 一個類有且只有一個析構函數。若未顯示定義,系統會自動生成默認的析構函數;
  4. 對象生命週期結束時,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操作符(參數列表);

注意:

  1. 不能通過連接其他符號來創建新的操作符:比如operator@
  2. 重載操作符必須有一個類類型或者枚舉類型的操作數
  3. 用於內置類型的操作符,其含義不能改變,例如:內置的整型+,不 能改變其含義 作爲類成員的重載函數時,其形參看起來比操作數數目少1成員函數的 操作符有一個默認的形參this,限定爲第一個形參
  4. .* 、:: 、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 ; // 日
}  

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章