C++-類與對象(2)

類與對象(第二部分)

類的6個默認成員函數

如果一個類中什麼成員都沒有,簡稱爲空類。空類並不是什麼都沒有,任何類在我們不寫的情況下,都會自動生成6個默認成員函數

類1

構造函數

構造函數的概念

構造函數是一個特殊的成員函數,名字與類名相同,創建類類型對象時由編譯器自動調用,保證每個數據成員都有一個合適的初始值,並且在對象的聲明週期內只調用一次。

構造函數的特性

構造函數是特殊的成員函數,需要注意的是,構造函數雖然名稱叫構造,但是它的主要任務並不是開空間創建對象,而是初始化對象。

特徵如下:

​ 1、函數名與類名相同

​ 2、無返回值

​ 3、對象實例化時編譯器自動調用對應的構造函數

​ 4、構造函數可以重載

class Date{
public:
    //無參構造函數
    Date(){
        
    }
    //帶參構造函數
    Date(int year, int month, int day){
        _year = year;
        _month = month;
        _day = day;
    }
private:
    int _year;
    int _month;
    int _day;
};
void Test(){
    Date d;
    Date d1(1900,1,1);
    //注意:通過無參構造函數創建對象時,對象後面不用跟括號,否則就成了函數聲明
    //以下代碼的函數,聲明瞭d3函數,該函數無參,返回一個日期類型的對象
    Date d3();
}

5、如果類中沒有顯式定義構造函數,則C++編譯器會自動生成一個無參的默認構造函數,一旦用戶顯示定義,那麼編譯器將不在生成。

class Date
{
public:
 /*
 // 如果用戶顯式定義了構造函數,編譯器將不再生成
 Date (int year, int month, int day)
 {
 _year = year;
 _month = month;
 _day = day;
 }
 */
private:
 int _year;
 int _month;
 int _day;
};
void Test()
{
 // 沒有定義構造函數,對象也可以創建成功,因此此處調用的是編譯器生成的默認構造函數
    Date d;
}

6、默認構造函數只能有一個,不管是無參的,還是全缺省的。

// 默認構造函數
class Date
{ 
public:
 Date()
 {
 _year = 1900 ;
 _month = 1 ;
 _day = 1;
 }
 
 Date (int year = 1900, int month = 1, int day = 1)
 {
 _year = year;
 _month = month;
 _day = day;
 }
private :
 int _year ;
 int _month ;
 int _day ;
};
// 以下測試函數能通過編譯嗎?
void Test()
{
 Date d1;
}

類2

在VS2013下可以編譯通過,說明在VS內編譯器對這種重載錯誤並不是特別在意。

但是在g++中,出現了編譯器不知道執行哪個構造函數。

7、關於編譯器生成的默認成員函數,我們可能會想到這個默認的構造函數有什麼用?我們直接聲明一個對象,返回的_year值都是隨機數,那麼還有什麼意義呢?

C++把類型分成內置類型和自定義類型。內置類型就是語法已經定義好的類型,如int/char…;自定義類型就是我們通過class/struct/union等定義的類型

#include <iostream>                       
class Time    
{    
  public:    
    Time()    
    {    
      std::cout << "Time()" << std::endl;    
      // 比特科技    
      //   8. 成員變量的命名風格    
      _hour = 0;    
      _minute = 0;    
      _second = 0;    
    }    
  private:    
    int _hour;    
    int _minute;    
    int _second;    
};    
class Date    
{    
  private:    
    // 基本類型(內置類型)    
    int _year;    
    int _month;    
    int _day;    
    // 自定義類型    
    Time _t;    
};
int main()
{
  Date d;
  return 0;
}     

8、成員變量風格

通過以上代碼能看出,在C++中,我們一般定義成員變量時,會在成員變量之前加上一個下劃線

int _year;
int _month;
int _day;

析構函數

析構函數的概念

與構造函數功能相反,析構函數不是完成對象的銷燬,局部對象銷燬工作由編譯器來完成。而對象在銷燬時會自動調用析構函數,完成類的一些資源清理工作。

析構函數的特性

1、析構函數名就是構造函數名之前加上字符~

2、無參數無返回值

3、一個類只有一個析構函數。若未顯示定義,系統會自動生成默認的析構函數

4、對象生命週期結束時,C++編譯系統自動調用析構函數

#include <iostream>    
#include <assert.h>    
typedef int DateType;    
class SeqList    
{                     
public:    
  SeqList(int capacity = 10){    
    _pData = (DateType*)malloc(capacity * sizeof(DateType));    
    assert(_pData);    
    _size = 0;    
    _capacity = capacity;    
  }    
  ~SeqList()    
  {    
    if(_pData)    
    {    
      free(_pData);  //釋放棧上資源  
      _pData = NULL;    
      _size = 0;    
      _capacity = 0;    
    }    
  }    
private:    
  int* _pData;    
  size_t _size;    
  size_t _capacity;    
}; 

5、編譯器自動生成的默認析構函數,對會自定類型成員調用它的析構函數

class String
{
public:
 String(const char* str = "jack")
 {
 	_str = (char*)malloc(strlen(str) + 1);
 	strcpy(_str, str);
 }
 ~String()
 {
 	cout << "~String()" << endl;
 	free(_str);
 }
private:
 	char* _str;
};
class Person
{
private:
 	String _name;
 	int _age;
};
int main()
{
 	Person p;
 	return 0;
}

拷貝構造函數

拷貝構造函數概念

只有單個形參,該形參是對本類類型對象的引用(一般常用const修飾),在用已存在的類類型對象創建新對象時,新對象由編譯器自動調用

拷貝構造函數的特徵

1、拷貝構造函數是構造函數的一個重載

2、拷貝構造函數的參數只有一個且必須使用引用傳參,使用傳值的方式會引發無窮遞歸調用

類3

3、若未顯示定義,系統生成默認的構造拷貝函數。默認的拷貝構造函數對象按內存存儲字節序完成拷貝,這種拷貝我們叫做淺拷貝,或者值拷貝

#include <iostream>    
class Date    
{    
  public:    
    Date(int year = 1900, int month = 1, int day = 1)    
    {    
      _year = year;    
      _month = month;    
      _day = day;    
    }    
    void Print(){    
      std::cout<<_year<<"-"<<_month<<"-"<<_day<<std::endl;    
    }    
  private:    
    int _year;    
    int _month;    
    int _day;    
};                    
int main()    
{             
  Date d1;    
  // 這裏d2調用的默認拷貝構造完成拷貝,d2和d1的值也是一樣的。    
  Date d2(d1);                                                   
  d2.Print();                                                    
  return 0;                                                      
}

賦值運算符重載

運算符重載

C++爲了增強代碼的可讀性引入了運算符重載,運算符重載是具有特殊函數名的函數,也具有其返回值類型,函數名字以及參數列表,其返回值類型與參數列表與普通的函數類似。

函數名字爲:關鍵字operator後面接需要重載的運算符符號

函數原型:返回值類型operator操作符(參數列表)

注意:

  • 不能通過連接其他符號來創建新的操作符:比如operator@
  • 重載操作符必須有一個類類型或者枚舉類型的操作數
  • 用於內置類型的操作符,其含義不能改變,例如:內置的整型+,不能改變其含義
  • 作爲類成員的重載函數時,其形參看起來比操作數數目少1成員函數的
  • 操作符有一個默認的形參this,限定爲第一個形參
  • .* 、:: 、sizeof 、?: 、. 注意以上5個運算符不能重載。這個經常在筆試選擇題中出現。
// 全局的operator==
class Date
{ 
public:
 Date(int year = 1900, int month = 1, int day = 1)
 {
 _year = year;
 _month = month;
 _day = day;
 } 
//private:
 int _year;
 int _month;
 int _day;
};
// 這裏會發現運算符重載成全局的就需要成員變量是共有的,那麼問題來了,封裝性如何保證?
// 這裏其實可以用我們後面學習的友元解決,或者乾脆重載成成員函數。
bool operator==(const Date& d1, const Date& d2)
{
 return d1._year == d2._year;
 && d1._month == d2._month
 && d1._day == d2._day;
}
void Test ()
{
 Date d1(2018, 9, 26);
 Date d2(2018, 9, 27);
 cout<<(d1 == d2)<<endl;
}
class Date
{ 
public:
 Date(int year = 1900, int month = 1, int day = 1)
 {
 _year = year;
 _month = month;
 _day = day;
 }
 // bool operator==(Date* this, const Date& d2)
 // 這裏需要注意的是,左操作數是this指向的調用函數的對象
 bool operator==(const Date& d2)
 {
 return _year == d2._year;
     && _month == d2._month
 	&& _day == d2._day;
 }
private:
 int _year;
 int _month;
 int _day;
};
void Test ()
{
 Date d1(2018, 9, 26);
 Date d2(2018, 9, 27);
 cout<<(d1 == d2)<<endl;
}
賦值運算符重載
class Date
{ 
public :
 Date(int year = 1900, int month = 1, int day = 1)
 {
 	_year = year;
 	_month = month;
 	_day = day;
 }
 Date (const Date& d)
 {
 	_year = d._year;
 	_month = d._month;
 	_day = d._day;
 }
 
 Date& operator=(const Date& d)
 {
 	if(this != &d)
 	{
 		_year = d._year;
 		_month = d._month;
 		_day = d._day;
 	}
 }
private:
 int _year ;
 int _month ;
 int _day ;
};

賦值運算符主要有四點:

1、參數類型

2、返回值

3、檢測是否自己給自己賦值

4、返回*this

5、一個類如果沒有顯示定義賦值運算符重載,編譯器也會生成一個,完成對象按字節序的值拷貝

const成員

將const修飾的類成員函數稱之爲const成員函數,const修飾類成員函數,實際修飾該函數隱含的this指針,表明在該成員函數中不能對類的任何成員進行修改

取地址及const取地址操作符重載

這兩個默認成員函數一般不用重新定義,編譯器默認會生成

class Date
{
public :
 Date* operator&()
 {
 	return this ;
 }
 const Date* operator&()const
 {
 	return this ;
 }
private :
 int _year ; // 年
 int _month ; // 月
 int _day ; // 日
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章