C++類別別的定義和使用

2020-07-16 10:04:22
在 C++ 中,類的定義方法如下:

class  類名{
存取範圍說明符:
    成員變數1
    成員變數2
    成員函數宣告1
    成員函數宣告2

存取範圍說明符:
    更多成員變數
    更多成員函數宣告
    ...
};

類的定義要以;結束。

“存取範圍說明符”一共有三種,分別是 public、private 和 protected。三者的區別後面會詳細介紹,目前暫且都用 public。“存取範圍說明符”可以出現任意多次。

“成員變數”的寫法與普通的變數定義相同。稱其為成員變數,是因為這些變數是一個類的成員。

同樣地,“成員函數宣告”的寫法也與普通的函數宣告相同。

一個類的成員函數之間可以互相呼叫。類的成員函數可以過載,也可以設定引數的預設值。

以前所學的函數不是任何類的成員函數,可稱為“全域性函數”。

成員變數就代表物件的“屬性”,成員函數就代表物件的“方法”。成員變數和成員函數出現的先後次序沒有規定。

成員函數的實現可以位於類的定義之外,格式如下:

返回值型別  類名:函數名()
{
    函數體
}


定義類之後,就可以定義物件了。定義物件的基本方法如下:

類名  物件名;

此處,“物件名”的命名規則和普通變數相同。物件也可以看作“類變數”。

類的範例程式剖析

下面來看一個用物件導向的方法進行 C++ 程式設計的例子。

例題:編寫一個程式,輸入矩形的寬度和高度,輸出其面積和周長。

這個程式比較簡單,實際上根本不需要用物件導向的方法進行設計,這裡只是為了使讀者更容易理解類和物件的概念。

首先要做的事情是分析問題中有哪些“物件”。比較明顯,只有“矩形”這種物件。然後要進行“抽象”,即概括“矩形”這種物件的共同特點。

矩形的屬性就是寬度和高度。因此需要兩個變數,分別代表寬度和高度。

一個矩形可以有哪些方法(即可以對矩形進行哪些操作)呢?在本程式中,矩形可以有設定寬度和高度、計算面積和計算周長三種行為,這三種行為可以各用一個函數實現,它們都會用到寬度和高度這兩個變數。

“抽象”完成後,就可以用 C++ 提供的語法特性寫出一個“矩形類”,將矩形的屬性和方法“封裝”在一起。程式如下:
#include <iostream>
using namespace std;
class CRectangle
{
public:
    int w, h; //成員變數,寬和高
    void init( int w_,int h_ ); //成員函數,設定寬和高
    int area(); //成員函數, 求面積
    int perimeter(); //成員函數,求周長
}; //必須有分號
void CRectangle::init( int w_,int h_ )
{
    w = w_;  h = h_;
}
int CRectangle::area()
{
    return w * h;
}
int CRectangle::perimeter()
{
    return 2 * ( w + h);
}
int main( )
{
     int w,h;
     CRectangle  r;  //r是一個物件
     cin >> w >> h;
     r.init( w,h);
     cout << "It's area is " << r.area() << endl;
     cout << "It's perimeter is " << r. perimeter();
       cout << sizeof(CRectangle) << endl;
     return 0;
}
第 3~10 行定義了一個類,類的名字就是 CRectangle。在 C++ 中,每個類的名字都是一種型別,可以像 int、double 等基本型別那樣來使用。因此,CRectangle 也是一種程式設計師自己定義的型別的名字。

CRectangle 類的定義中宣告了它有哪些成員變數和成員函數。第 5行 的 public 就是類成員的存取範圍說明符,它說明了下面的成員都是公有成員(具體含義稍後解釋)。

第 11~22 行是 CRectangle 類各個成員函數的具體實現。

定義 CRectangle 類後,就可以定義 CRectangle 型別的變數。在 C++ 中,通過類定義出來的變數稱為“物件”。例如,第 26 行定義了一個 CRectangle 型別的變數 r,由於 CRectangle 是一個類,因此將 r 稱為一個“物件”。

一般來說,在 C++ 中,一個物件佔用的記憶體空間的大小等於其成員變數所佔用的記憶體空間的大小之和。例如,一個 CRetangle 物件佔用 8 個位元組,因為它的兩個成員變數 w 和 h 分別 佔用 4 個位元組。sizeof(CRectangle) 表示式的值是 8。

每個物件都有各自的儲存空間。一個物件的某個成員變數被改變後,不會影響另一個物件。成員函數並非每個物件各自存有一份。成員函數和普通函數一樣,在記憶體中只有一份,但是它可以作用於不同的物件。

使用類的成員變數和成員函數可以用物件名.成員名的方式。例如:
CRectangle r1, r2;
r1.w = 5;
r2.init(5, 4);
r1.w = 5;這條語句直接對 r1 物件的 w 成員變數進行賦值。它不會影響 r2。

物件名.成員函數名的方式可以呼叫成員函數,並且被呼叫成員函數是作用在該物件上的。例如,r2.init(5, 4);這條語句就呼叫了 CRectangle 類的 init 成員函數,該成員函數執行時是作用在 r2 這個物件上的。

所謂成員函數作用在某個物件上,指的是進入該成員函數時,函數中存取到的成員變數是屬於該物件的。例如,r2.init(5, 4);這條語句在 init 函數執行期間存取的 w 和 h 就是屬於 r2 這個物件的。執行r2.init(5, 4);不會影響 r1。

同理,在上面的程式中,第 29 行和第 30 行的 area 和 perimeter 成員函數都是作用在 r 物件上 的,返回的就是 r 的面積和周長。

存取物件的成員

除了前面提到的物件名.成員名方法,還可以用指標->成員名的方式來存取物件的成員。例如:
CRectangle rl, r2;
CRectangle* p1 = &r1;
CRectangle* p2 = &r2;
p1 -> w = 5;  //此處的 w 屬於 p1 指向的物件
p2 -> init(5, 4);  //init函數作用在 p2 指向的物件上

還可用參照名.成員名的方式存取物件的成員。例如:
CRectangle r2;
CRectangle & rr = r2;
rr.w = 5;
rr. init(5, 4);  //rr的值改變,r2的值也改變

呼叫類的成員函數時,必須指明其所作用的物件。對於上面的 CRectangle 類來說,如果只寫init(2, 4);這條語句是不能編譯通過的。因為編譯器不清楚這個 init 函數是作用在哪個物件上面的,進入 init 函數以後,其中存取的 w 和 h 的記憶體地址在哪裡無從知曉,因而也就無法編譯了。

類還有另外一種寫法,就是將 class 關鍵字換成 struct。例如:
struct CRectangle{
public:
    int w, h;  //成員變數,寬度和高度
    void init(int w_, int h_);  //成員函數,設定寬度和高度
};
沒有成員函數的 struct 還是稱作“結構”,結構變數不是物件;有成員函數的 struct 就是類。

寫成 struct 的類和寫成 class 的類只在類成員的可存取範圍方面有一點很小的差別,後面很快會提到。

和結構變數一樣,物件之間可以用互相賦值,但不能用 ==、!=、<、>、<=、>= 進行比較運算,除非這些運算子經過適當的“過載”。運算子過載是後面要學的知識,此處不予解釋。