'C++|數據抽象與數據抽象的實現(類)'

數據結構 設計 技術 算法 電視機 小智雅匯 2019-09-02
"

為了理解問題,需要將不必要的細節分離出去,你將構造你自己針對問題的抽象視圖,抽象模型。這個建模過程稱為抽象 abstraction。

模型定義了一個抽象的視圖,該視圖只關注於與問題和求解者相關的部分。你需要定義下列屬性:影響的數據;識別出來的操作; ADT 就是一種模型。

"

為了理解問題,需要將不必要的細節分離出去,你將構造你自己針對問題的抽象視圖,抽象模型。這個建模過程稱為抽象 abstraction。

模型定義了一個抽象的視圖,該視圖只關注於與問題和求解者相關的部分。你需要定義下列屬性:影響的數據;識別出來的操作; ADT 就是一種模型。

C++|數據抽象與數據抽象的實現(類)

1. 它提供了一種類型.

2. 它提供了操作的集合. 該集合被稱為接口.

3. 接口的操作是類型數據結構的唯一訪問途徑.

4. 原理和前提定義了類型的應用領域.

數據抽象是指數據結構或數據表示的設計者只向外界提供關鍵信息,並隱藏其後臺的實現細節,即只表現必要的信息而不呈現細節。而讓使用者只關注公開、關鍵的接口信息。

數據抽象是一種依賴於接口和實現分離的編程(設計)技術。

讓我們舉一個現實生活中的真實例子,比如一臺電視機,作為使用者的您可以打開和關閉、切換頻道、調整音量、添加外部組件(如喇叭、錄像機、DVD 播放器),但是您不知道它的內部實現細節,也就是說,您並不知道它是如何通過纜線接收信號,如何轉換信號,並最終顯示在屏幕上,這些都是電視機設計者的工作。

因此,我們可以說電視把它的內部實現和外部接口分離開了,作為使用者的您無需知道它的內部實現原理,直接通過它的外部接口(比如電源按鈕、遙控器、聲量控制器)就可以操控電視。

例如,您的程序可以調用 sort() 函數,而不需要知道函數中排序數據所用到的算法。實際上,函數排序的底層實現會因庫的版本不同而有所差異,可以是快速排序,也可以是冒泡、選擇、插入或其它類型的排序算法,只要接口不變,函數調用就可以照常工作。

就C編程來說,其結構化編程的方法也是一種模塊分解的方法,通過函數來實現模塊化,函數通過三種控制結構來實現某一分解的功能,是一種粗淺的數據與對數據操作的封裝技術,通過函數聲明在頭文件中實現接口,在實現文件中實現函數實現,也實現了粗淺的接口與實現的分離。但函數與操作的數據封裝不夠緊密,對數據的訪問沒有適當的控制方法,且數據與操作這些數據的函數無法實現適當的內聚與耦合,也就是沒有抽象出類類型這個概念。

就 C++ 編程而言,C++ 類類型為數據抽象提供了可能。它們向外界提供了大量用於操作對象數據的公共方法,也就是說,外界實際上並不清楚類的內部實現(包括的私有數據及成員函數的具體實現)。

"

為了理解問題,需要將不必要的細節分離出去,你將構造你自己針對問題的抽象視圖,抽象模型。這個建模過程稱為抽象 abstraction。

模型定義了一個抽象的視圖,該視圖只關注於與問題和求解者相關的部分。你需要定義下列屬性:影響的數據;識別出來的操作; ADT 就是一種模型。

C++|數據抽象與數據抽象的實現(類)

1. 它提供了一種類型.

2. 它提供了操作的集合. 該集合被稱為接口.

3. 接口的操作是類型數據結構的唯一訪問途徑.

4. 原理和前提定義了類型的應用領域.

數據抽象是指數據結構或數據表示的設計者只向外界提供關鍵信息,並隱藏其後臺的實現細節,即只表現必要的信息而不呈現細節。而讓使用者只關注公開、關鍵的接口信息。

數據抽象是一種依賴於接口和實現分離的編程(設計)技術。

讓我們舉一個現實生活中的真實例子,比如一臺電視機,作為使用者的您可以打開和關閉、切換頻道、調整音量、添加外部組件(如喇叭、錄像機、DVD 播放器),但是您不知道它的內部實現細節,也就是說,您並不知道它是如何通過纜線接收信號,如何轉換信號,並最終顯示在屏幕上,這些都是電視機設計者的工作。

因此,我們可以說電視把它的內部實現和外部接口分離開了,作為使用者的您無需知道它的內部實現原理,直接通過它的外部接口(比如電源按鈕、遙控器、聲量控制器)就可以操控電視。

例如,您的程序可以調用 sort() 函數,而不需要知道函數中排序數據所用到的算法。實際上,函數排序的底層實現會因庫的版本不同而有所差異,可以是快速排序,也可以是冒泡、選擇、插入或其它類型的排序算法,只要接口不變,函數調用就可以照常工作。

就C編程來說,其結構化編程的方法也是一種模塊分解的方法,通過函數來實現模塊化,函數通過三種控制結構來實現某一分解的功能,是一種粗淺的數據與對數據操作的封裝技術,通過函數聲明在頭文件中實現接口,在實現文件中實現函數實現,也實現了粗淺的接口與實現的分離。但函數與操作的數據封裝不夠緊密,對數據的訪問沒有適當的控制方法,且數據與操作這些數據的函數無法實現適當的內聚與耦合,也就是沒有抽象出類類型這個概念。

就 C++ 編程而言,C++ 類類型為數據抽象提供了可能。它們向外界提供了大量用於操作對象數據的公共方法,也就是說,外界實際上並不清楚類的內部實現(包括的私有數據及成員函數的具體實現)。

C++|數據抽象與數據抽象的實現(類)

封裝(Data & Operations)--- 信息隱藏的技術——對象的用戶不能看到對象的數據和操作的細節.

數據抽象 ---- 從對象中發現類的過程

抽象數據類型----

在 C++ 中,我們使用類來定義我們自己的抽象數據類型(ADT)。

數據封裝是一種把數據和操作數據的函數捆綁在一起的機制,數據抽象是一種僅向用戶暴露接口而把具體的實現細節隱藏起來的機制。

C++ 通過創建來支持封裝和數據隱藏(包括public、protected、private的訪問控制)。

封裝是一種信息隱蔽技術,其目的是使對象的使用者和設計者分離,對類的定義和實現分開。

C++ 程序中,任何帶有公有和私有成員的類都可以作為數據封裝和數據抽象的實例。

在類類型中,其數據成員及成員函數內聚於一種類類型,成員函數操作數據成員,其作用域侷限於類類型,類類型可以通過繼承與組合實現一定的耦合。

您可以使用類 iostream 的 cout 對象來輸出數據到標準輸出,如下所示:

#include <iostream>
using namespace std;
int main( )
{
\tcout << "Hello C++" <<endl;
\treturn 0;
}

在這裡,您不需要理解 cout 是如何在用戶的屏幕上顯示文本。您只需要知道公共接口即可,cout 的底層實現可以自由改變。

在 C++ 中,我們使用訪問標籤來定義類的抽象接口。一個類可以包含零個或多個訪問標籤:

使用公共(public)標籤定義的成員都可以訪問該程序的所有部分。一個類型的數據抽象視圖是由它的公共成員來定義的,呈現為數據抽象的接口部分。

使用私有(private)標籤定義的成員無法訪問到使用類的代碼。私有部分對使用類型的代碼隱藏了實現細節。

訪問標籤出現的頻率沒有限制。每個訪問標籤指定了緊隨其後的成員定義的訪問級別。指定的訪問級別會一直有效,直到遇到下一個訪問標籤或者遇到類主體的關閉右括號為止。

數據抽象有兩個重要的優勢:

1 類的內部受到保護,不會因無意的用戶級錯誤導致對象狀態受損。

2 類實現可能隨著時間的推移而發生變化,以便應對不斷變化的需求,或者應對那些要求不改變用戶級代碼的錯誤報告。

如果只在類的私有部分定義數據成員,編寫該類的設計者就可以隨意更改數據。如果實現發生改變,則只需要檢查類的代碼,看看這個改變會導致哪些影響。如果數據是公有的,則任何直接訪問舊錶示形式的數據成員的函數都可能受到影響。

C++ 程序中,任何帶有公有和私有成員的類都可以作為數據抽象的實例。請看下面的實例:

#include <iostream>
using namespace std;
class Adder{
public:
\tAdder(int i = 0)// 構造函數
\t{
\t\ttotal = i;
\t}
\tvoid addNum(int number)// 對外的接口
\t{
\t\ttotal += number;
\t}
\tint getTotal()// 對外的接口
\t{
\t\treturn total;
\t};
private:
\tint total;// 對外隱藏的數據
};
int main( )
{
\tAdder a;
\t
\ta.addNum(10);
\ta.addNum(20);
\ta.addNum(30);
\t
\tcout << "Total " << a.getTotal() <<endl;
\treturn 0;
}

當上面的代碼被編譯和執行時,它會產生下列結果:

Total 60

上面的類把數字相加,並返回總和。公有成員 addNum 和 getTotal 是對外的接口,用戶需要知道它們以便使用類。私有成員 total 是用戶不需要了解的,但又是類能正常工作所必需的。

抽象把代碼分離為接口和實現。所以在設計組件時,必須保持接口獨立於實現,這樣,如果改變底層實現,接口也將保持不變。

在這種情況下,不管任何程序使用接口,接口都不會受到影響,只需要將最新的實現重新編譯即可。

如果一個類只是做為接口存在,而實現由其派生類去實現的話,這樣的類稱為抽象類,類中至少有一個函數被聲明為純虛函數,純虛函數是通過在聲明中使用 "= 0" 來指定的,如下所示:

class Box
{
public:
// 純虛函數
virtual double getVolume() = 0;
private:
double length; // 長度
double breadth; // 寬度
double height; // 高度
};

設計抽象類(通常稱為 ABC)的目的,是為了給其他類提供一個可以繼承的適當的基類。抽象類不能被用於實例化對象,它只能作為接口使用。如果試圖實例化一個抽象類的對象,會導致編譯錯誤。

因此,如果一個 ABC 的子類需要被實例化,則必須實現每個虛函數,這也意味著 C++ 支持使用 ABC 聲明接口。如果沒有在派生類中重寫純虛函數,就嘗試實例化該類的對象,會導致編譯錯誤。

可用於實例化對象的類被稱為具體類

#include <iostream>
using namespace std;
// 基類
class Shape
{
public:
\t// 提供接口框架的純虛函數
\tvirtual int getArea() = 0;
\tvoid setWidth(int w)
\t{
\t\twidth = w;
\t}
\tvoid setHeight(int h)
\t{
\t\theight = h;
\t}
protected:
\tint width;
\tint height;
};
// 派生類
class Rectangle: public Shape
{
public:
\tint getArea()
\t{
\t\treturn (width * height);
\t}
};
class Triangle: public Shape
{
public:
\tint getArea()
\t{
\t\treturn (width * height)/2;
\t}
};
int main(void)
{
\tRectangle Rect;
\tTriangle Tri;
\tRect.setWidth(5);
\tRect.setHeight(7);
\t// 輸出對象的面積
\tcout << "Total Rectangle area: " << Rect.getArea() << endl;
\tTri.setWidth(5);
\tTri.setHeight(7);
\t// 輸出對象的面積
\tcout << "Total Triangle area: " << Tri.getArea() << endl;
\treturn 0;
}

當上面的代碼被編譯和執行時,它會產生下列結果:

Total Rectangle area: 35
Total Triangle area: 17

從上面的實例中,我們可以看到一個抽象類是如何定義一個接口 getArea(),兩個派生類是如何通過不同的計算面積的算法來實現這個相同的函數。

面向對象的系統可能會使用一個抽象基類為所有的派生類和對象提供一個適當的、通用的、標準化的接口。然後,派生類通過繼承抽象基類,就把所有類似的操作都繼承下來。

外部接口提供的功能(即公有函數)在抽象基類中是以純虛函數的形式存在的。這些純虛函數在相應的派生類中被實現。

這個架構也使得新的應用程序可以很容易地被添加到系統中,即使是在系統被定義之後依然可以如此。

-End-

"

相關推薦

推薦中...