你好,C++(32) 類是對現實世界的抽象和描述 6.2.1 類的聲明和定義

6.2  類:當C++愛上面向對象

類這個概念是面向對象思想在C++中的具體體現:它既是封裝的結果,同時也是繼承和多態的載體。所以,要想學習C++中的面向對象程序設計,也就必須從「類」開始。程序員

6.2.1  類的聲明和定義

面向對象思想把現實世界中的全部事物都當作是對象,而類是對全部相同類型對象的抽象,是對它們整體的一個描述。好比,學校有不少老師,張老師、李老師、王老師,雖然每一個老師各不相同,是不一樣的對象個體。但他們都是老師這一類型的對象,有着共同的屬性(都有姓名、職務)和相同的行爲(都能上課、批改做業)。咱們把某一類型對象的共同屬性和相同行爲抽象出來,分別用變量和函數加以描述,而後把這些變量和函數用類這個概念封裝起來,就成了能夠用來描述這一類對象的新的數據類型,這種新的數據類型所以也被稱爲類。在C++中,聲明一個類的語法格式以下:函數

class 類名
{
public :
    // 公有成員,一般用來描述這類對象的相同行爲
protected:
    // 保護型成員
private:
    // 私有成員,一般用來描述這類對象的共同屬性
};  // 注意這裏有個分號表示類的結束

其中,class是C++中用以聲明類的關鍵字,其後跟所要聲明的類的名字,一般是某個能夠歸納這一類對象的名詞。其命名規則相似於以前介紹的變量名命名規則。這裏,咱們要定義一個類來描述「老師」這類對象,因此咱們用「Teacher」做爲這個類的名字。學習

在後面的章節中,咱們還將學到C++中的類還有基類與派生類之分,它是面向對象思想的繼承機制在類當中的體現。若是這個類是從某個基類繼承而來的,咱們在「class 類名」後面還要加上這個類的繼承方式(public、protected或private)以及它所繼承的基類的名字。這樣,聲明一個類的語法格式相應地就變爲:spa

class 類名:繼承方式 基類名
{
    // 成員變量和成員函數的聲明…
};

若是某個類沒有繼承關係,則類聲明中的繼承方式能夠省略。這裏的Teacher類自己就是基類,並非由其餘類繼承而來,因此這裏繼承方式應當省略。設計

完成類的名字及繼承關係的定義後,就能夠開始在類的主體中描述這個類的屬性和行爲了。對象的屬性屬於數據,因此咱們在類聲明中定義一些變量來描述對象的屬性。好比,「老師」這類對象擁有姓名這個屬性,因此咱們就能夠定義一個string類型的變量strName來描述。這些變量描述了對象的屬性,成爲了這個類總體的一部分,因此也被稱爲成員變量。code

最佳實踐:在類聲明中給成員變量初始值對象

若是類的某些成員變量具備初始值,咱們能夠在類中聲明這些成員變量的同時給它一個初始值,這樣在運行期間,類就能夠在進入構造函數以前,直接使用這個初始值完成相應成員變量的初始化。例如:blog

class Teacher
{
// 具備初始值的成員變量
protected:
    // 用字符串常量「Name」做爲成員變量m_strName的初始值
    string m_strName = 「Name」;       // 姓名
private:
    // 用常數2000做爲成員變量m_unBaseSalary的初始值
    unsigned int m_unBaseSalary = 2000;
};

在這段代碼中,咱們用兩個常量分別做爲類的兩個成員變量m_strName和m_unSalary的初始值。通過這樣的聲明以後,在建立這個類的對象時,無需在構造函數中進行額外的初始化,它的這兩個成員變量就會擁有相應的初始值。這一特性能夠用於那些全部類的對象都擁有相同初始值的狀況,好比全部「Teacher」對象的「m_unBaseSalary」(基本工資)都是2000元。繼承

除了聲明成員變量來描述對象的屬性以外,對象的另一個重要組成部分就是它的行爲。在C++中,咱們用函數來描述一個行爲動做。一樣,咱們也將函數引入類中成爲它的成員函數,用來描述類對象的行爲。好比,一個「老師」對象有備課的行爲動做,咱們就能夠爲老師這個類添加一個PrepareLesson()函數,在這個函數中能夠對老師備課動做進行具體的定義。類的構成如圖6-7所示。接口

 

                       

圖6-7  類的構成

最佳實踐:爲類設計對程序員友好的接口

咱們所設計的類不只供咱們本身使用,更多時候它還會提供給其餘程序員使用,以達到代碼複用或者實現團隊協做的目的。這時,類的接口設計的好壞,將會影響到他人可否正確並輕鬆地使用咱們所設計的類。所以,它也成爲了衡量一個程序員水平高低的重要標準。

類的接口,就像類的使用說明書同樣,是向類的使用者說明它所須要的資源及它所可以提供的服務等。只要類的接口對程序員友好,從接口就能夠輕鬆地知道如何正確地使用這個類。要作到這一點,應當遵照下面這些設計原則。

l  遵循變量與函數的命名規則

成員變量也是變量,成員函數也是函數。因此,做爲類的接口的它們,在命名的時候也一樣應該遵照廣泛的命名規則,讓它們的名字可以準確而簡潔地表達它們的含義。

l  簡化類的視圖

接口,表明了類所可以向用戶提供的服務。因此,在進行類的接口設計時,只須要將必要的成員函數公有(public)就能夠了,使用受保護的(protected)或者私有的(private)成員來向用戶隱藏沒必要要的細節。由於隱藏了用戶不該該訪問的內容,天然也就減小了用戶犯錯誤的機會。

l   使用用戶的詞彙

類設計出來最終是讓用戶使用的,因此在設計類的接口時,應該從用戶的角度出發,使用用戶所熟悉的詞彙,讓用戶在閱讀類的接口時,不須要學習新的詞彙或概念,這樣能夠平滑用戶的學習曲線,讓咱們的類使用起來更容易。

除了在類中定義變量和函數來表示類的屬性和行爲以外,還可使用public、protected及private這三個關鍵字來對這些成員進行修飾,指定它們的訪問級別。按照訪問級別的不一樣,類的全部成員被分紅了三個部分。一般,使用public修飾的成員外界能夠訪問,咱們會在public部分定義類的行爲,提供公共的函數接口供外部訪問;使用protected修飾的成員只有類本身和它的派生類能夠訪問,因此在protected部分,咱們能夠定義遺傳給下一代子類的屬性和行爲;最後private修飾的成員只有類本身能夠訪問,因此在private部分,咱們能夠定義這個類所私有的屬性和行爲。關於類的繼承方式和訪問控制,稍後將進行詳細介紹。這裏先來看一個實際的例子。例如,要定義一個類來描述老師這一類對象,經過對這類對象的抽象,咱們發現老師這類對象擁有隻有本身和子類能夠訪問的姓名屬性和你們均可以訪問的上課行爲。固然,老師還有不少其餘屬性和行爲,這裏根據須要做了簡化。最後,咱們使用面向對象的封裝機制,將這些屬性和行爲捆綁到一塊兒,就有了老師這個類的聲明。

// 老師
class Teacher
{
// 成員函數
// 描述對象的行爲
public: // 公有部分,供外界訪問
    void GiveLesson();     // 上課
// 成員變量
// 描述對象的屬性
protected:// 受保護部分,本身和子類訪問
    string m_strName;           // 姓名
private:
};

經過這段代碼,咱們聲明瞭一個Teacher類,它是全部老師這種對象的一個抽象描述。這個類有一個public關鍵字修飾的成員函數GiveLesson(),它表明老師這類對象擁有你們均可以訪問的行爲——上課。它還有一個protected關鍵字修飾的變量m_strName,表示老師這類對象的只有它本身和子類能夠訪問的屬性——姓名。這樣,經過在一個類中聲明函數描述對象的行爲,聲明變量描述對象的屬性,就完整地聲明瞭一個能夠用於描述某類對象的類。

完成類的聲明以後,咱們還須要對類的行爲進行具體的定義。類成員函數的具體定義能夠直接在類中聲明成員函數的時候同時完成:

class Teacher
{
// 成員函數
// 描述對象的行爲
public:
    // 聲明成員函數的同時完成其定義
    void GiveLesson()     
    {
        cout<<"老師上課。"<<endl;
    };

//

};

更多時候,咱們只是將類的聲明放在頭文件(好比,Teacher.h文件)中,而將成員函數的具體實現放在類的外部定義,也就是相應的源文件(好比,Teacher.cpp)中。在類的外部定義類的成員函數時,咱們須要在源文件中引入類聲明所在的頭文件,而且在函數名以前還要用「::」域操做符指出這個函數所屬的類。例如:

#ifndef _TEACHER_H  // 定義頭文件宏,防止頭文件被重複引入
#define _TEACHER_H  // 在稍後的7.3.1小節中會有詳細介紹
// Teacher.h  類的聲明文件
class Teacher
{
    //
public:
    void GiveLesson();  // 只聲明,不定義
};

#endif


// Teacher.cpp  類的定義文件
// 引入類聲明所在的頭文件
#include "Teacher.h"
// 在Teacher類外完成成員函數的定義
void Teacher::GiveLesson()
{
    cout<<"老師上課。"<<endl;
}

這裏能夠看到,成員函數的定義跟普通函數並沒有二致,一樣都是用函數來完成某個動做,只是成員函數所表示的是某類對象的動做。例如,這裏只是輸出一個字符串表示老師上課的動做。固然,在實際應用中,類成員函數還能夠對成員變量進行訪問,所完成的動做也要比這複雜得多。

 

知道更多:C++中用以聲明類的另外一個關鍵字——struct

在C++中,要聲明一個類,除了使用正牌的「class」關鍵字以外,以前在3.8節中介紹過的用來定義結構體的「struct」關鍵字也一樣能夠用來聲明一個類。在語法上,「class」和「struct」很是類似,二者均可以用來聲明類,而二者惟一的區別就是,在沒有指定訪問級別的默認狀況下,用「class」聲明的類當中的成員是私有的(private),而用「struct」聲明的類當中的成員是公有的(public)。例如:

// 使用「struct」定義一個Rect類
struct Rect
{
    // 沒有訪問權限說明
// 類的成員函數,默認狀況下是公有的(public)
    int GetArea()
    {
        return m_nW * m_nH;
    }

    // 類的成員變量,默認狀況下也是公有的(public)
    int m_nW;
    int m_nH;
};

這裏,咱們使用「struct」聲明瞭一個Rect類,由於沒有使用public等關鍵字顯式地指明類成員的訪問控制,在默認狀況下,類成員都是公有的,因此能夠直接訪問。例如:

Rect rect;
// 直接訪問成員變量
rect.m_nH = 3;
rect.m_nW = 4;

// 直接訪問成員函數
cout<<"Rect的面積是:"<<rect.GetArea()<<endl;

這兩個關鍵字的默認訪問控制要麼過於保守,要麼過於開放,這種「一刀切」的方式顯然沒法適應於全部狀況。因此不管是使用「class」仍是「struct」聲明一個類,咱們都應該在聲明中明確指出各個成員的合適的訪問級別,而不該該依賴於關鍵字的默認行爲。

「class」和「struct」除了上面這點在類成員默認訪問級別上的差別以外,從「感受」上講,大多數程序員都認爲它們仍有差別:「struct」僅像一堆缺少封裝的開放的內存位,更多時候它是用以表示比較複雜的數據;而「class」更像活的而且可靠的現實實體,它能夠提供服務、有牢固的封裝機制和定義良好的接口。既然你們都這麼「感受」,那麼僅僅在類只有不多的方法而且有較多公有數據時,才使用「struct」關鍵字來聲明類;不然,使用「class」關鍵字更合適。

相關文章
相關標籤/搜索