1. 程式人生 > >c++的類的封裝/繼承/多型的簡單介紹

c++的類的封裝/繼承/多型的簡單介紹

 本篇文章僅僅從很表層來介紹一個C++語言中的類,包括什麼是類,類的封裝性/繼承性和多型性。高手直接跳過吧,看了浪費時間,新手或者想溫習一下的可以瀏覽看看。

1. 什麼是類?

到底什麼是類(class)??類就是一種型別,是使用者自己定義的一個型別,和內建型別如int/float/double類似,  用一個類可以去定義一個變數,即課本中所謂的類的例項化,會得到一個object。類這個型別比較特別,它即包括了資料(資料成員),又包含了若干個操作這些資料的方法(即成員函式)。為什麼需要類呢?類提供了一種對事物的抽象,增強了事物的聚合性,類讓我們可以把一個事物當作一個整體去看,方便描述,方便建模。 例如:我們可以定義一個學生的類:包括了姓名/性別/年齡/學號ID等資訊

  1 class Student
  2 {
  3 public:
  4         int GetID();
  5         string GetName();
  6         string GetSex();
  7         int GetAge();
  8         
  9         void SetID(int ID_); 
 10         void SetName(string Name_); 
 11         void SetSex(string Sex_); 
 12         void SetAge(int
Age_); 13 14 private: 15 string m_Name; 16 string m_Sex; 17 int m_ID; 18 int m_Age 19 };

如上所示,我們定義了一個Student的類, 定義它之後,編譯器就不光知道了Student是一個型別,而且知道了型別的一些細節,例如當使用該型別去定義一個變數(object)時,需要分配多少記憶體等,例如:

1     // 輸入Student型別在記憶體中佔多少位元組                                                     
2 cout << "Student型別的大小為:" << sizeof(Student) << endl; 3 4 //例項化一個叫小明的學生,並命名為SB(有點矛盾) 5 Student xiaoming; 6 xiaoming.SetName("SB"); 7 cout << xiaoming.GetName() << endl;

接下來,詳細說說如何定義一個類:

定義一個類,需要做的是:1. 宣告類擁有的資料成員, 2. 宣告類擁有的成員函式:

1 class Dog
2 {
3 public: 
4     int Age;
5     int GetAge();
6 };

成員函式在哪裡定義呢?即可以在類的內部直接定義,也可以在類的外部進行定義(此時,需要指明所屬的類)。當定義在類的內部時,預設宣告為inline函式。 當類外部定義成員函式時,可以有類內宣告為inline函式,也可以在定義時候聲明瞭inline函式,但是個人更喜歡在類外定義的時候宣告為inline函式,這樣可以根據自己定義一個函式的實際情況,決定是否宣告為inline函式,而不需要提前考慮好。

 1 // 類的內部定義
 2 class Dog 
 3 {
 4 public: 
 5     int Age;
 6     int GetAge()    //當定義在類的內部時,預設宣告為inline函式
 7     {
 8         return Age;
 9     }
10 };
11 
12 // 類外部定義
13 class Dog
14 {                                                                                                                                                                                               
15 public: 
16     int Age;
17     int GetAge();
18 };
19 
20 Dog::GetAge()
21 {
22     return Age;
23 }
24 
25 // 類外部定義,顯示宣告為inline函式
26 class Dog
27 {
28 public: 
29     int Age;
30     inline int GetAge();
31 };

另外:

1. 類成員函式通過隱式this指標訪問類內部的資料成員的; 如果把this指標修飾為const,在成員函式後面加const ,這就是const 成員函式:  GetAge() const 
2.  編譯器在解析一個類,總是先把類內部的資料成員解析完畢,再去處理成員函式,因此,定義一個類時,不必關心資料成員與成員函式的先後位置,資料成員聖成員函式問題可見的。
3. 一個類域也是一個作用域(用{ }括起來的部分),正因為如此, 1. 在類的外部定義成員函式時,指定類名就進入了類的作用域中了,就可以找到資料成員了; 2. 對類內的靜態資料成員與靜態成員函式,可以通過類名作用域訪問(對非靜態的不可以,因為非靜態的資料成員屬於具體的一個物件而不屬於類,雖然非靜態的成員函式實際上在多個物件之間是共享的,但是也只能通過物件名物件的指標訪問,因為它們有隱式的this指標)

2. 類的封裝性

 封裝性就是說可以把一部分東西封裝起來,不讓別人看到。在C++中,類這種型別通過它的訪問控制符來體現了它的封裝性,包括:

public: 公有的,在類的外部放著,誰都可以看到;
protected: 保護性的,只讓它的子類可以看到;
private: 私有的,即把它們封裝在了類的內部,在類的外面是看不到的,只有類內部的人可以看到;

例如:定義了一個House的類,House外面的只能看到pulic下的內容,而在House裡面,可以看到所有內容;

 1 class House
 2 {
 3 public:                                                                                      
 4     int Windows;
 5     void OpenWindow();
 6     int doors;
 7     void OpenDoors();
 8 
 9 protected:
10     int *** // 這個不好舉例子
11 
12 private:
13     int Desk;
14     int light;
15     void OpenLight();
16 }

    使類具有封裝性的目的是:我們可以定義一些類,只對類的使用者留一下公有的介面(即pulic下的內容),而類內部的相關操作對類的使用者來說是透明的,使用者不操心。我可以隨便改類內部的程式碼,只有公有的介面不變,類的使用者的程式碼是不需要調整的。保證資料安全,方便使用者使用,大家都省心啊。

3. 類的繼承

    如果我們想在一個類的基礎上繼續建立一個新類,這就用到了類的繼承性。繼承可以使用三個訪問標誌符控制:public、protectd和private。  無論哪個繼承,對直接子類沒有任何影響,只對子類的使用者有影響。基類中的private成員無論使用哪個訪問標誌符,子類的使用者是看不到的,基類的public與protected成員是否能讓子類的使用者看到由三種訪問標誌符控制 。

public繼承: 基類內的資料成員與成員函式封裝特性不變在子類中不變
protected繼承: 基類內的資料成員與成員函式的public部分在子類中變為protected
private繼承: 基類內的資料成員與成員函式的public和protected部分變為private

例如:

 1 class Base
 2 {
 3 public:
 4     *****
 5 protected:
 6     *****
 7 };
 8                                                                                 
 9 // 公有繼承
10 class Derived1 : public Base
11 {
12 public:
13     .....
14 private:
15     ....
16 };
17 
18 // 私有繼承
19 class Derived2 : private Base
20 {
21 public:
22     ;;;;;;
23 };

a. 基類中虛擬函式和純虛擬函式

    思考這麼一個問題: 當基類定義了某個函式,而在子類中又定義了一個同名的函式(返回型別與引數列表可以不同),這時會發生什麼?
答: 子類內的該函式會隱藏基類中同名的函式。即使他們的引數列表與返回型別不同,也不會發生過載,因為過載必須在相同的作用域內發生,如果作用域不同,編譯器查詢時,總會先找到最近作用域內的同名函式。

    很多時候,我們想在基類與子類中定義相同的介面(即同名函式),它們實現各自的功能,這就可以把這樣的函式宣告為虛擬函式,即virtual.   當通過類的指標與引用呼叫虛擬函式時,會發生動態繫結,即多型。如下面例子所示:

 1 // 程式
 2 #include<iostream>                                                              
 3 using namespace std;
 4 
 5 class Base
 6 {
 7 public:
 8     virtual void Say() {cout << "I am Base!" << endl;}
 9 };
10 
11 class Derived : public Base
12 {
13 public:
14     virtual void Say() {cout << "I am Derived!" << endl;}
15 };
16 
17 int main()
18 {
19     Base* pA = new Base;
20     Derived* pB = new Derived;
21     pA->Say();
22     pB->Say();
23 }
24 
25 // 輸出
26 [email protected]:~/c$ ./a.out 
27 I am Base!
28 I am Derived!

    當我們不想例項化一個類時,我們可以定義一個抽象基類,它只負責提供介面。包含純虛擬函式的類為抽象類。純虛擬函式必須在子類中進行宣告與定義。而虛擬函式可以不在子類中宣告與定義,這時候它會像普通成員函式一樣繼承基類中的虛擬函式的實現。

1 class Base
2 {
3 public:                                                                         
4     virtual void Say() = 0;     //純虛擬函式
5 }; 

總結來說:

1. 當我們要繼承一個類的介面與實現時,我們在基類中定義普普通通的成員即可。
2. 當我們想要繼承一個類的介面與預設的實現時,我們在基類中定義為虛擬函式。
3. 當我們只要繼承一個類的介面時,我們在基類中定義為純虛擬函式。

其它說明:

1. 在子類中,當我們重新宣告和定義虛擬函式時,可以加上virtual關鍵字(virtual只能用在類內,不可以把virtual 用在類外),也可以不加, 在c++11標準中,引入了override關鍵字來顯示錶示覆蓋基類中虛擬函式的定義,override關鍵字有利於給編譯器更多資訊,用於查錯。例如當我們在子類中定義了一個與基類中虛擬函式名字相同,但是引數列表不同的函式,我們本意是定義子類特有的虛擬函式版本,來覆蓋基類中的版本。然而這時候,基類與子類中的函式是獨立的,只是基類中的版本隱藏了而已。如果使用了override,編譯器發現沒有覆蓋,就會報錯。 如果我們不想讓基類中的某個虛擬函式被覆蓋掉,可以使用final關鍵字。(另外覆蓋,即override只會發生在虛擬函式身上

2. 如果我們定義 了一個類,並且不想該類被繼承,可以在定義這個類時,在類名後面加上final關鍵字。

 1 class Base
 2 {
 3 public:
 4     virtual void Say() {cout << "I am Base!" << endl;}
 5     virtual void Dad() final { cout << " I am your dad!" << endl;}   //對該虛擬函式使用final關鍵字
 6 };
 7 
 8 class Derived final : public Base       // 對類Derived 使用了final 關鍵字                                                                                                                       
 9 {
10 public:
11     void Say() override { cout << " I am Derived!" << endl;}        //使用了override關鍵字
12 };

3. 雖然一個純虛擬函式不需要定義,但是其實我們是可以定義一個純虛擬函式的,不過呼叫它的唯一途徑是”呼叫時明確指出它的class的名稱“。

 1 // 程式
 2 class Base
 3 {
 4 public:
 5     virtual void Hello() = 0;
 6 };
 7 void Base::Hello() {cout << "hello " << endl;}                                                                                                                                                  
 8 
 9 class Derived final : public Base
10 {
11 public:
12     void Hello() override {cout << "a,很疼的" << endl;}
13 };
14 
15 int main()
16 {
17     Derived* pB = new Derived;
18     pB->Hello();
19     pB->Base::Hello();
20 }
21 
22 // 輸出
23 [email protected]:~/c$ ./a.out 
24 a,很疼的
25 hello 

4. 基類與子類中的虛擬函式的返回型別和引數列表必須完全一致,如果不一致的話,編譯器認為他們是完全不相關的函式。他們之間不會發生覆蓋(override),子類中的同名函式只會隱藏子類中的同名函式。

b. 繼承中的作用域

關於類的作用域,我們要明白以下幾點:

1. 類本身是一個作用域,使用{ }括起來的。
2. 在類的繼承過程中,子類的作用域是巢狀在基類的作用域之內的(這就明白了為什麼有時候子類中的成員函式會隱藏掉基類中函式,就時候如果我們想要使用被隱藏的基類函式,可以通過顯示指明類名(這時可以理解為作用域名)來訪問。
3. 在一個類的作用域中,編譯器在解析類時,它總會先解析類中宣告的所以資料成員與成員函式,再去解析成員函式的定義。正因為這樣的原因,無論資料成員定義在成員函式的後面還是前面,還是成員函式的順序前後之類的, 一個成員函式總是可以找到該類的資料成員或呼叫其它成員函式。

基於作用域的一個例子:

 1 // 程式
 2 class Base
 3 {
 4  public:
 5     virtual void Hello() { cout << "Hello, I am Base!" << endl; }
 6     void Hi() { cout << "Hi, Hi, Base!" << endl;}
 7 };
 8 
 9 class Derived : public Base
10 {
11 public:
12     void Hello() override { cout << "Hello, I am Derived!" << endl;}
13     void Hi() { cout << "Hi, Hi, Derived!" << endl;}
14 };
15 
16 int main()
17 {
18     Derived Derive;
19     Derive.Hello();
20     Derive.Hi();
21     Derive.Base::Hello();        //顯示呼叫基類的虛擬函式
22     Derive.Base::Hi();            // 顯示呼叫基類普通函式
23 
24     Base *pBase = &Derive;
25     pBase->Hello();        //指標呼叫,進行動態繫結,即多型
26     pBase->Base::Hello();     //顯示呼叫基類的虛擬函式
27     pBase->Base::Hi();          // 顯示呼叫基類普通函式
28 
29     return 0;
30 }
31 
32 //程式輸出:
33 Hello, I am Derived!
34 Hi, Hi, Derived!
35 Hello, I am Base!
36 Hi, Hi, Base!
37 Hello, I am Derived!
38 Hello, I am Base!
39 Hi, Hi, Base!

額外小知識點:

1. 待後續遇到補充!

相關推薦

C實現封裝繼承

開發十年,就只剩下這套架構體系了! >>>   

C#之封裝繼承

面向物件程式設計的三大特性:封裝、繼承、多型。   封裝:封裝是實現面向物件程式設計的第一步,封裝就是將資料或函式等集合在一個個的單元中(我們稱之為類),被封裝的物件通常被稱為抽象資料型別。作用是保護資料不被其它的函式意外的修改。 //利用get與set控制器我們可以定義一些儲存屬

C#基礎封裝繼承

一、封裝 C#中可使用類來達到資料封裝的效果,這樣可以使資料與方法封裝成單一元素,以便於通過方法存取資料。除此之外,還可以控制資料的存取方式。 在面向物件程式設計中,大多數都是以類作為資料封裝的基本單位。類將資料和操作資料的方法結合成一個單位。設計類時,不希

C#08(封裝繼承)

封裝,繼承,多型 一、封裝 C#中可使用類來達到資料封裝的效果,這樣可以使資料與方法封裝成單一元素,以便於通過方法存取資料。除此之外,還可以控制資料的存取方式。 在面向物件程式設計中,大多數都是以類作為資料封裝的基本單位。類將資料和操作資料的方法結合成一個單位。

黑馬程式設計師—Objective-C學習—封裝繼承

三大特性:成員變數的封裝、繼承、多型 一、封裝 寫成員變數時,不要寫@public,應使用set方法。成員變數儘量不要用@public,不讓外界直接訪問應提供一個方法給外界設定和訪問成員變數的值。即set方法和getf方法。 1、set方法 a、作用:提供一個方法給外界

Objective-c封裝繼承

面向物件的三個基本特徵是:封裝、繼承、多型。 封裝 簡介 封裝是實現面向物件程式設計的第一步,封裝就是將資料或函式等集合在一個個的單元中(我們稱之為類)。被封裝的物件通常被成為抽象資料型

C++面向物件程式設計——概述(物件、、抽象、封裝繼承

前言:今天第一次上C++課程。根據老師的所講內容進度,記錄C++知識!!! 第一章 問題一:什麼是面向物件程式設計? 面向物件程式設計是一種新的程式設計範型。主要特徵是:程式=物件+訊息 面向物件程式設計的

OC學習篇之---的三大特性 封裝繼承

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

補習:C# 面向物件三大特性:封裝繼承

C# 面向物件三大基本特性 封裝、繼承、多型 1、封裝 隱藏物件的屬性和實現細節,僅對外公開介面,控制在程式中屬性的讀取和修改的訪問級別。 C# 封裝根據具體的需要,設定使用者的訪問許可權,並通過 訪問修飾符 來實現。 一個 訪問修飾符 定義了一個類成員的範圍和可

八、C#入門基礎08(封裝繼承)

一、封裝 C#中可使用類來達到資料封裝的效果,這樣可以使資料與方法封裝成單一元素,以便於通過方法存取資料。除此之外,還可以控制資料的存取方式。 在面向物件程式設計中,大多數都是以類作為資料封裝的基本單位。類將資料和操作資料的方法結合成一個單位。設計類時,不希望直接存取類中的資料,而是希

python封裝繼承

#案例:小智今天想出去,但不清楚今天的天氣是否適宜出行,需要一個幫他提供建議的程式,程式要求輸入daytime#和night,根據可見度和溫度給出出行建議和使用的交通工具,需要考慮需求變更的可能#需求分析:使用python類的封裝、繼承、多型比較容易實現,由父類封裝檢視可見度和檢視溫度的方法,#子類繼承父類。

Java之路:封裝繼承

面向物件有三大特點:封裝性、繼承性和多型性。 一、封裝 1、封裝的含義 封裝 (Encapsulation)是將描述某類事物的資料與處理這些資料的函式封裝在一起,形成一個有機整體,稱為類。 類所具有的封裝性可使程式模組具有良好的獨立性與可維護性,這對大型程式的開發是特別重要的

C#】之 封裝繼承

我們知道封裝、繼承和多型是面向物件方法設計中的三大基本特性,下面將具體講解這三個特性的具體表現及意義。 #一、封裝 ##1、說明   從字面意思上看,封裝就是打包的意思,將什麼包裝起來,專業一點就是資訊的隱藏,將物件的屬性和方法打包成一個相對獨立的單位,儘可能隱蔽物件的內部細

JAVA基礎第三章-與物件、抽象、介面 JAVA基礎第一章-初識java JAVA基礎第二章-java三大特性:封裝繼承

 業內經常說的一句話是不要重複造輪子,但是有時候,只有自己造一個輪子了,才會深刻明白什麼樣的輪子適合山路,什麼樣的輪子適合平地! 我將會持續更新java基礎知識,歡迎關注。   往期章節: JAVA基礎第一章-初識java JAVA基礎第二章-java三大特

C++】C++封裝繼承小結

轉載地址:http://blog.csdn.net/ruyue_ruyue/article/details/8211809 面向物件的三個基本特徵 面向物件的三個基本特徵是:封裝、繼承、多型。其中,封裝可以隱藏實現細節,使得程式碼模組化;繼承可以擴充套件

Java面向物件筆記(封裝繼承、抽象、介面)

面向物件的思想在現實生活中很常見,例如專案開發,老闆想要實現某個功能,但是他自己不會這些技術,所以他就會讓員工去做這些事情,通過藉助別的擁有你想要實現某些功能的過程就可以理解為面向物件。 面向物件有三大特性:封裝、繼承、多型。 1.封裝:即是把函式或者工具類的實

Python全棧開發記錄_第九篇(的基礎_封裝_繼承_

  有點時間沒更新部落格了,今天就開始學習類了,今天主要是類的基礎篇,我們知道面向物件的三大特性,那就是封裝,繼承和多型。內容參考該部落格https://www.cnblogs.com/wupeiqi/p/4493506.html   之前我們寫的都是函式,可以說是面向過程的程式設計,需要啥功能就直接寫啥,

JAVA基礎第四章-集合框架Collection篇 JAVA基礎第一章-初識java JAVA基礎第二章-java三大特性:封裝繼承 JAVA基礎第三章-與物件、抽象、介面 記一次list迴圈刪除元素的突發事件!

 業內經常說的一句話是不要重複造輪子,但是有時候,只有自己造一個輪子了,才會深刻明白什麼樣的輪子適合山路,什麼樣的輪子適合平地! 我將會持續更新java基礎知識,歡迎關注。   往期章節: JAVA基礎第一章-初識java JAVA基礎第二章-java三大特性

JAVA基礎第五章-集合框架Map篇 JAVA基礎第一章-初識java JAVA基礎第二章-java三大特性:封裝繼承 JAVA基礎第三章-與物件、抽象、介面 JAVA基礎第四章-集合框架Collection篇

 業內經常說的一句話是不要重複造輪子,但是有時候,只有自己造一個輪子了,才會深刻明白什麼樣的輪子適合山路,什麼樣的輪子適合平地! 我將會持續更新java基礎知識,歡迎關注。   往期章節: JAVA基礎第一章-初識java

Python基礎(12):面向物件基礎(,例項,封裝繼承

一,什麼是面向物件? 面向物件,Object Oriented Programming,簡稱oop。是一種程式設計思想。 中心:將物件作為程式的基本單元。一個物件包含了資料和操作資料的函式。 python,一切皆物件。 二,類和例項 類,class,抽象的模板。 例項,i