-
類
(編程術語)
鎖定
類基本信息
類的實質是一種引用數據類型,類似於 byte、short、int(char)、long、float、double 等基本數據類型,不同的是它是一種複雜的數據類型。因為它的本質是數據類型,而不是數據,所以不存在於內存中,不能被直接操作,只有被實例化為對象時,才會變得可操作。
類是對現實生活中一類具有共同特徵的事物的抽象。如果一個程序裏提供的數據類型與應用中的概念有直接的對應,這個程序就會更容易理解,也更容易修改。一組經過很好選擇的用户定義的類會使程序更簡潔。此外,它還能使各種形式的代碼分析更容易進行。特別地,它還會使編譯器有可能檢查對象的非法使用。
[2]
類的內部封裝了屬性和方法,用於操作自身的成員。類是對某種對象的定義,具有行為(behavior),它描述一個對象能夠做什麼以及做的方法(method),它們是可以對這個對象進行操作的程序和過程。它包含有關對象行為方式的信息,包括它的名稱、屬性、方法和事件。
類的構成包括成員屬性和成員方法(數據成員和成員函數)。數據成員對應類的屬性,類的數據成員也是一種數據類型,並不需要分配內存。成員函數則用於操作類的各項屬性,是一個類具有的特有的操作,比如“學生”可以“上課”,而“水果”則不能。類和外界發生交互的操作稱為接口。
類特性
類類的三大特性
- 繼承性更符合認知規律,使程序更易於理解,同時節省不必要的重複代碼。
類類與結構體的區別
- 從職能觀點來看,class 表現為行為;而 struct 常用於存儲數據。
- class 實例由垃圾回收機制來保證內存的回收處理;而 struct 變量使用完後立即自動解除內存分配。
- 作為參數傳遞時,class 變量是以按址方式傳遞;而 struct 變量是以按值方式傳遞的。
我們可以簡單的理解,class 是一個可以動的機器,有行為,有多態,有繼承;而 struct 就是個零件箱,組合了不同結構的零件。其實,class 和 struct 最本質的區別就在於 class 是引用類型,內存分配於託管堆;而 struct 是值類型,內存分配於線程的堆棧上。由此差異,導致了上述所有的不同點。所以只有深刻的理解內存分配的相關內容,才能更好的駕馭。
那麼,有人不免會提出,既然 class 幾乎可以完全替代 struct 來實現所有的功能,那麼 struct 還有存在的必要嗎……答案是,至少在以下情況下,鑑於性能上的考慮,我們應該考慮使用 struct 來代替 class:
類用法
定義一個類
class 類名 { public: 公有成員 private: 私有成員 protected: 保護成員 };
- 結尾部分的分號必不可少,否則會發生語法錯誤。
- 無論公有成員、私有成員還是保護成員,彼此之間都可以訪問。比如公有的成員函數可以操作保護的數據成員,也可以調用私有的成員函數。
- 類的數據成員是類型,所以不能被賦值,聲明數據成員和聲明普通變量的格式相同,比如 “ int n; ”。
成員函數的實現
類構造函數與析構函數
- 函數名固定構造函數和析構函數的函數名必須是類名。
- 重載的特殊性構造函數和普通成員函數一樣可以被重載,析構函數不可以重載,只能是空參數。
即使顯式地定義構造函數和析構函數,也還是會有默認的構造函數和析構函數,函數內部無任何語句,不執行任何操作。默認構造函數是無參數的。需要注意的是,一旦顯式定義任意形參的構造函數,默認構造函數都不會生成,即只有沒有定義構造函數的類才存在默認構造函數。
一般情況下,默認的構造函數和析構函數可以滿足功能需要,然而當需要重載構造函數,或是需要動態分配資源的時候,就不得不定義自己的構造函數甚至析構函數了。
類類的實例化
就像聲明某種類型的變量一樣,聲明一個類類型的對象,就是類的實例化,會涉及到必要的內存分配。
不同語言中類的實例化形式是不同的。
- Java / C# 類名 對象名 = new 類名(參數列表);括號不能省略。
對象可以訪問類的成員,但並不是所有成員都可以被訪問,能否訪問取決於聲明該成員時所用的關鍵字(public / protected / private)。具體規則如下:
- 類的公有成員可以被該類,其派生類和類實例化的對象訪問。
- 類的保護成員可以被該類及其派生類訪問,不可以被該類的對象訪問。
- 類的私有成員可以被該類訪問,不可以被派生類及其該類的對象訪問。
類派生與繼承
class子類類名: public父類類名,private父類類名, protected父類類名 { public: 公有成員 private: 私有成員 protected: 保護成員 };
派生和繼承是類的重要特性,繼承是由抽象到具體的體現。通過繼承,子類可以使用父類的成員。
但要注意的是,派生和繼承在帶來方便的同時,也會使類與類之間的關係變得複雜,尤其是涉及到私有繼承和保護繼承時,類中成員的關係可能會變得難以理解。所以在涉及類時,儘量避免過多層次的繼承,私有繼承和保護繼承的使用也要慎重。
繼承來的成員和自身聲明的成員並無本質區別,也有公有成員、私有成員、保護成員之分。繼承時,父類中成員類型(公有成員 / 私有成員 / 保護成員)和繼承方式(公有繼承 / 私有繼承 / 保護繼承)不同,情況不同。可以歸納為:
三種類型的繼承,父類的成員均被子類繼承(之前的百科關於這點的描述是錯誤的),只是由類實例化的對象對其繼承的成員的訪問權限會有所變化。三種不同方式的繼承,描述的是子類實例化對象對其成員的訪問權限,並非是描述子類時,子類對繼承自父類的成員的訪問權限。
- 公有繼承繼承自父類的成員保持不變。
- 私有繼承繼承自父類的成員全部變為私有成員。
- 保護繼承繼承自父類的公有成員變為保護成員,其餘不變。
類操作符重載
操作符重載必須在類中進行,重載操作符可以使操作符對在類中的語義發生變化。除了. ,.* ,:: ,? : 、sizeof、typeid這幾個運算符不能重載之外,大部分運算符都能被重載。但要注意,重載操作符並不能改變操作符的優先級和結合律,而且從認知規律上講,重載的操作符功能必須與原意相近,否則很難被人理解。
操作符重載是函數,在使用該操作符時被調用。操作符重載函數的聲明形式:返回值 operator操作符(參數列表);
類友元
通常,類中的私有成員只能被自身使用,無法被它的對象訪問。因此,另一個類即便可以使用該類的對象,也無法訪問該類的私有成員,通過定義友元的方法可以做到這一點。
友元就是在一個類中“再次聲明”另一個類的成員函數或是另一個類,被“再次聲明”的成員函數或類可以訪問該類的私有成員。這種“再次聲明”並不是普通的聲明,格式為:friend 函數 / 類名;
顯然,友元會破壞類的封裝性,使本該隱藏的成員暴露出來,因此應當謹慎使用。
類組合
繼承可以描述 “ 交通工具 ” 和 “ 公交車 ” 的關係,卻無法描述 “ 公交車 ” 和 “ 車輪 ” 的關係。
大多數 “ 車輪 ” 具有的特性是 “ 公交車 ” 所不具有的。比如説 “ 車輪 ” 具有 “ 重量 ”,而 “ 公交車 ” 的 “ 重量 ” 則是另一個含義。而通過私有成員、保護成員機制控制這些成員的繼承性,會使繼承變得複雜而難以理解。而且繼承來的數據成員只有一個,而一輛 “ 公交車 ” 卻有四個 “ 車輪 ”,四個 “ 車輪 ” 的 “ 重量 ”。
類抽象類
並不是所有種類的事物都可以被實例化,換而言之,有的種類只是一種抽象概念,現實中並沒有實際存在的對應物。比如:假設所有的動物都會叫,我們可以定義一個類——“ 動物 ”,定義類中的一個成員函數——“ 叫 ”,我們知道貓的叫聲,也知道狗的叫聲,然而 “ 動物 ” 是如何 “ 叫 ” 的——我們根本無法實現它。所以,我們引入了抽象類的概念,抽象類是無法被實例化的,無法聲明抽象類的對象。
抽象類只能被用作基類,作用體現於:
類靜態成員
靜態數據成員的初始化必須在類外進行,使用靜態成員時必須使用類名和域操作符。
類示例
類的複用
class Animal { private void beat() { System.out.println(""); } public void breath() { beat(); System.out.println("") } } class bird { // privateAnimala; public Bird(Animala) { this.a=a; } // pubilc void breath() { // a.breath(); } public void fly() { System.out.println("") } } class Wolf { // private Animala; }
- 參考資料
-
- 1. 類 .漢典網[引用日期2020-01-14]
- 2. Bjarne Stroustrup.C++程序設計語言(譯名):機械工業出版社,2002:199-200