C++基礎學習第一天

這行程式碼沒Bug發表於2021-03-11

類與物件

struct與class

聚合類

最初的C++稱為“帶類的C”,擴充套件了C語言的很多功能。在C++語言中,仍然可以使用C語言中的struct定義結構。

struct 結構名{
    型別 變數名;
    型別 變數名;
    ...
}

在C++中,聚合類是一種特殊型別的,使用者可以直接訪問其成員,並且具有特殊的初始化語法形式(可以提供一對花括號括起來的成員初始值列表初始化聚合類的資料成員)。

聚合類應當滿足的條件:

  • 所有的成員都是public型別的(任何情況下,物件都可以通過成員運算子訪問成員);
  • 沒有定義任何建構函式;
  • 沒有類內初始值;
  • 沒有基類,也沒有虛擬函式。

舉個例子,定義一個聚合類Data

struct Data{
    int ival;
    string str;   
};

初始化可以這樣:

Data dval = {8,"Smith"};

注意:初始值的順序必須與成員宣告的順序一致。初始值列表中元素的個數如果少於類的成員數量,則靠後的成員被值初始化。初始值列表中元素的個數絕對不能少於類的成員數量。

C++對struct的擴充套件

C++中的struct不僅可以包含資料,還可以包含函式。

舉個例子:

#include<iostream>

using namespace std;
struct Complex{
    double real;
    double imag;
    void init(double r,double i){
        real = r;
        imag = i;
    }
    double getReal(){return real;}
    double getImag(){return imag;}
};

int main(){
    Complex c;
    c.init(2,3);
    cout <<c.getReal() <<"+" <<c.getImag() <<"i" <<endl;
    return 0;
}

小提示:在結構中資料成員後面帶分號,函式成員後面是不帶分號的,但是函式成員內需要帶分號,結構反括號後面也是要加分號的,main結束是不加分號的。

說明:結構中的資料和函式稱為成員,realimag是資料成員。定義Complex結構後,就可以用它來定義變數,並能通過成員運算子訪問它的成員函式。

訪問許可權

為了實現資訊隱藏,限制對結構中某些成員的非法訪問,C++增高了3個訪問許可權限定符,用於設定結構中資料成員和函式成員的訪問許可權。

  • public

    被設定為public許可權的成員(包括資料成員和函式成員)稱為類的公有成員,可被任何函式訪問(包括結構內和結構外的函式)。

  • private

    被設定為private許可權的成員(包括資料成員和函式成員)稱為類的私有成員,僅供結構(類)的內部(自身成員函式)訪問。

  • protected

    protected與繼承有關。供結構(類)的內部及後代(派生類)訪問。

在C和C++中,如果沒有宣告訪問許可權,預設的訪問許可權是public。如果不想讓外部直接訪問需要加個private宣告為私有型別。C++結構的定義形式:

struct 類名{
    [public:]
    	成員;
    private:
    	成員;
    protected:
    	成員;
};

類(class)具有資訊隱藏的能力,能夠完成介面與實現的分離,用於把資料抽象的結果封裝成可以用於程式設計的抽象資料型別。是物件導向程式設計中通用的資料封裝工具。在C++中,class具有與struct完全相同的功能,用法一致。類是預設private

類也是一種自定義資料型別,用關鍵字class表示,用法與struct相同,形式如下:

class 類名{
    [private:]
    	成員;
    public:
    	成員;
    protected:
    	成員;
};

類名通常首字元大寫的識別符號表示 ;一對花括號界定了類的範圍;最後的分號必不可少,表示類宣告結束。

舉個例子:

class Complex
{
    private:
    	double real,imag;
    public:
    	void init(double r,double i){
            real = r;
            imag = i;
        }
    	double getReal(){return real;}
    	double getImag(){return imag;}
    	void print(){
        	cout <<c.getReal() <<"+" <<c.getImag() <<"i" <<endl;
    	}
};

對於類定義需要注意的問題:

  • 訪問說明符

    對於類宣告中的訪問說明符privatepublicprotected沒有先後主次之分,也沒有使用次數的限制。只是出於資訊隱藏的目的,將資料成員設定為private許可權,將需要讓外部函式訪問的成員設定為public許可權。

  • 類作用域

    classstruct後面的一對花括號包圍的區域是一種獨立的作用域,稱為類域。類域內的資料和函式都稱為成員(資料稱為資料成員,函式常被稱為成員函式)。同一類域內的成員是不愛訪問許可權和先後次序的限制,相互間可以直接訪問。

  • 關鍵字struct和class

    C++的struct也是一種類,它與class具有相同的功能,用法也是完全相同。唯一區別是在沒有指定成員的訪問許可權時,預設的許可權是不一樣的。

抽象與封裝

類是對客觀世界中同類事物的抽象,它給出了屬於該類事物共有的屬性(資料成員)和操作(成員函式)。

類具有封裝特性。

  • 能夠把資料和演算法組合在一起,構成一個不可分割的整體;
  • 能夠有效地把類的內部資料隱藏起來,外部函式只能通過類的公有成員才能訪問類的內部資料。

抽象是對具體物件(問題)進行概括,抓住問題的本質,抽出這一類物件共有的性質,並加以描述的過程,抽象主要包括資料抽象和過程抽象。

抽象與封裝的區別:抽象是一種思維方式,而封裝是一種基於抽象性的操作方法。一般通過抽象,把所得到的資料資訊以封裝的技術將其重新整合,形成一個新的有機體,這就是類。

類的成員

資料成員

類的資料成員可以是任何資料型別,如整型、浮點型、字元型、陣列、指標和引用等,也可以是另外一個類的物件或指向物件的指標,還可以是自身類的指標或引用,但不能是自身類的物件;可以是const常量,但不能是constexpr常量;可以用decltype推斷型別,但不能使用auto推斷型別;資料成員不能指定為extern的儲存類別。

C++支援在定義類的同時為類的資料成員賦初始值,舉個例子:

class X{
    private:
    	int a = 2;
    	int y = {3};
    	int b[3] = {1,2,3};
    	const int ci = a;
    public:
    	...
}

注意:類的定義或宣告只是在程式中增加的自定義資料型別,是沒有分配相應的記憶體空間。只有在例項化時,才會分配相應的空間,也才會在這時初始化資料成員。

成員函式

類的成員函式也稱為方法或服務。它可以在類內定義,也可以在類外定義;可以過載,也可以使用預設實參。

  • 成員函式的定義

    類的成員函式有兩種定義方式:

    • 宣告類時就給出成員函式的定義。

      class Complex
      {
          private:
          	double real,imag;
          public:
          	void init(double r,double i){
                  real = r;
                  imag = i;
              }
          	double getReal(){return real;}
          	double getImag(){return imag;}
      };
      
    • 在宣告類時,只宣告成員函式的原型,然後在外部定義成員函式。類外定義函式格式

      返回型別 類名::成員函式名(引數列表);
      

      其中,::是作用域運算子,用於說明這裡定義的函式是指定類中的一個成員函式。

      class Complex
      {
          private:
          	double real,imag;
          public:
          	void init(double r,double i);
          	double getReal();
          	double getImag();
      };
      void Complex::init(double r,double i){
          real = r;
          imag = i;
      }
      void Complex::getReal(){
          return real;
      }
      void Complex::getImag(){
          return imag;
      }
      
  • const(常量)成員函式

    為了禁止成員函式修改資料成員的值,可以將它設定為const(常量)成員函式。設定的方法是在成員函式形參列表的後面加上關鍵字const,格式如下:

    class X{
        T f(T1,T2,...) const;
    };
    

    說明:T是函式返回型別,f是函式名,T1T2是各參加的型別。將成員函式設定為const型別後,表明該成員函式不會修改任何成員的值。

    注意:只有類的成員函式才能指定為常量,一般的函式是一能的。const成員函式與const引數是不同的。

  • 成員函式的過載與預設實參

    類的函式是可以過載,也可以指定預設實參。

    在外定義的函式,只能在宣告時或類外定義成員函式時才能指定預設實參,但不能在宣告和定義中同時指定。

巢狀型別

巢狀型別是在類中定義的類、列舉、使用typedef宣告為成員的任意型別以及使用using宣告的型別別名。

  • 巢狀類

    • 定義在另一個類的內部的類稱作巢狀類,巢狀類也是一個獨立的類,與外層類基本沒有關係;
    • 外層類的物件和巢狀類的物件是相互獨立的,巢狀類物件中不包含任何外層類定義的成員,外層類也不包含巢狀類定義的成員;
    • 巢狀類的訪問許可權由外層類決定;
    • 外層類對巢狀類的成員沒有特殊的訪問許可權,巢狀類對外層類的成員也沒有訪問許可權;
    class X{
        ...
            class Y{       //Y是在X類定義的巢狀類
                ...
            }
    };
    
    • 巢狀類也可以在類內宣告,類外定義(需要外層類型別來限定)。
    class X{
          ...
          public:
           	class Y;      //Y是X的巢狀類,定義在外層類之外
     };
     class X::Y{          //Y是在X類外定義的巢狀類
         ...
     }
    
  • 型別別名

    在類內部可以使用typedefusing宣告型別別名。

    typedef T alias;
    using alias = T;
    

類型別與物件

通過class定義的型別稱為類型別,由類型別定義的變數稱為物件。

類型別

不同類型別的物件不能進行自動相互轉換,即使兩個類具有完全相同的成員列表,它們也是不同型別,是不能進行自動型別轉換。

可以直接把類名作為型別的名字來用,從而直接指向類型別。也可以把類名跟關鍵字後面來使用。

Complex c;         //預設初始化Complex型別的物件
class Complex c;   //等價宣告

類的宣告和定義是可以分開的,可以先宣告類,而暫時不定義它。

class C;   //C類的宣告

物件

類是對同類事物的一種抽象,這類事物中具體的一個例項就把它看作一個物件。類和物件的關係就是資料型別和變數的關係。

  • 物件的定義

    用類定義物件的形式如下:

    類型別名 物件名;
    

    類型別是一種自定義類型別,可以定義型別的變數,也可以定義類型別的指標和引用。

    Clock* p = &cl1;   //定義Clock類的指標p指向物件cl1
    Clock& r = cl2;    //定義Clock類的引用r指向物件cl2
    
  • 物件對成員的訪問

    類中成員之間可以相互訪問,不受訪問說明符的限制,可以直接使用成員名。類外使用只能使用“物件名.成員名”訪問具有public訪問說明符的成員。物件引用方法與結構相似,必須用成員運算子“.”作為物件名和物件成員之間的間隔符,格式如下:

    物件名.資料成員名
    物件名.成員函式名(實參列表)
    clk1.setHour(12);
    clk1.print();
    

    說明:類外只能訪問物件的公有成員,不能訪問物件的私有和受保護成員;如果定義的物件指標,在通過指標訪問物件成員時,要用成員運算子“->”作為指標物件和物件成員之間的間隔符。

  • 物件間的賦值

    不同類型別的物件之間不能互相賦值。同類的不同物件之間,以及同類的指標之間可以相互賦值。

    物件名1 = 物件名2;
    

    說明:進行賦值兩個物件型別必須相同;進行資料成員的值複製,賦值之後,兩不相干。