第10章 物件和類——物件和類(六) 抽象資料型別

superbmc發表於2024-03-25

本文章是作者根據史蒂芬·普拉達所著的《C++ Primer Plus》而整理出的讀書筆記,如果您在瀏覽過程中發現了什麼錯誤,煩請告知。另外,此書由淺入深,非常適合有C語言基礎的人學習,感興趣的朋友可以自行閱讀此書籍。

資料型別

我們先了解一下什麼是資料型別。

資料型別,是指一組性質相同的值的集合即定義在此集合上的一些操作的總稱。資料型別是按照值的不同進行劃分的。在高階語言中,每個變數、常量和表示式都有各自的取值範圍。型別就用來說明變數或表示式的取值範圍和所能進行的操作。

C++的資料型別有以下幾種:

  • 基本型別:也稱原子型別,該型別不可以再分解,包括整型、浮點型、字元型、布林型。
  • 複合型別:也稱結構型別,是由基本型別或其他複合資料型別組成的型別,是可以再分解的,包括陣列、指標、引用、結構體。
  • 列舉型別:列舉型別用於定義一組具有相關性的常量,這些常量被稱為列舉值。列舉值可以像整數一樣使用,但其取值被限定為預先定義的列舉列表中的一個。
  • 自定義資料型別:使用class和struct 關鍵字來建立自定義型別,這些型別可以包含自定義的資料成員和成員函式。

當我們自定義的資料型別表示的是一個抽象的而非具體的概念時,那麼就是抽象資料型別。

抽象資料型別

之前文章定義的Student類很具體,它儲存了學生的姓名、學號、各科成績,也可以計算總分和平均分。但是,我們也可以透過定義類來表示更通用的概念。

C++程式使用棧來管理自動變數。當新的自動變數被生成後,它們被新增到棧頂;消亡時,從棧中刪除它們。接下來,我們實現一個棧。

我們要設計一個棧類,首先要抽象出它的特徵,即就是資料 + 方法:
1,可以儲存多個資料項。
2,可以壓入資料。
3,可以彈出資料。
4,可以檢視是否為空。
5,可以檢視是否為滿。

根據特徵,我們可以寫出這樣的類宣告:

#ifndef _MY_STACK_H_
#define _MY_STACK_H_
class Stack
{
public:
  Stack();
  bool push(int x);         
  bool pop(int x);               //我們期望知道彈出的資料是什麼
  bool is_empty();
  bool is_full();
private:
  static const int MAX_SIZE = 20;
  int m_items[MAX_SIZE];
  int top;
};
#endif

我們使用了一個陣列來充當棧,這個陣列的大小被靜態常量限制為20。

這個類有些問題,首先,如果後續要修改儲存的元素的型別(當前是int型),那麼就需要直接修改這個類中的程式碼,以及push和pop的引數列表。我們可以把型別提出來,使用typedef取個別名,後續如果要修改棧中存放的資料型別,可以直接修改typedef處的程式碼。

另外,push和pop的引數可以改為引用,可以節省建立臨時變數的時間,對於push的引數我們不允許修改,因此也設定為const。

最後,判斷是棧是空的還是滿的,不應允許修改棧中的資料,因此需要將相關函式定義為const成員函式。

最佳化後的程式碼如下:

//stack.hpp
#ifndef _MY_STACK_H_
#define _MY_STACK_H_
typedef int Item; 
class Stack
{
public:
  Stack();
  bool push(const Item &x);         
  bool pop(Item &x);
  bool is_empty() const;
  bool is_full() const;
private:
  static const int MAX_SIZE = 20;
  Item m_items[MAX_SIZE];
  int top;                                  //總是指向棧頂元素+1的位置
};
#endif

我們再實現它的類方法:

//stack.cpp
#include "stack.hpp"

Stack::Stack()
{
  top = 0;
}

bool Stack::push(const Item &x)
{
  if(is_full())
  {
    return false;
  }
  m_items[top++] = x;
  return true;
}

bool Stack::pop(Item &x)
{
  if(is_empty())
  {
    return false;
  }
  x = m_items[--top];
  return true;
}

bool Stack::is_empty() const
{
  return top == 0;
}

bool Stack::is_full() const
{
  return top == MAX_SIZE;
}

我們再建立一個小程式使用下這個類:

#include <iostream>
#include <cctype>
#include "stack.hpp"
using namespace std;

int main()
{
  Stack st;
  char ch;
  int po;
  
  cout << "Please enter rA(a) to push , P(p) to pop, Q(q) to quit: " << endl;
  while(cin >> ch && toupper(ch) != 'Q')
  {
    while(cin.get() != '\n')
      continue;
    if(!isalpha(ch))
    {
      cout << '\a';
      continue;
    }
    switch(ch)
    {
      case 'A':
      case 'a':
                cout << "Enter a PO number to add: " ;
                while(!(cin >> po))
                {
                    cout << "Please enter a number!" ;
                    cout << endl;
                    cin.clear();
                    while (cin.get() != '\n')
                        continue;
                    cout << "Enter a PO number to add: " ;
                }
                if(st.is_full())
                {
                    cout << "stack already full" << endl;
                }
                else
                {  
                    st.push(po);
                }
                break;
      case 'P':
      case 'p':
                if(st.is_empty())
                {
                    cout << "stack already empty" << endl;
                }
                else
                {
                    st.pop(po);
                    cout << "PO #" << po << " poped" << endl;
                }
                break;    
    }
     cout << "Please enter A(a) to push , P(p) to pop, Q(q) to quit: " << endl;
  }
  cout << "Bye~" << endl;
  return 0;
}

程式執行結果如下:

Please enter rA(a) to push , P(p) to pop, Q(q) to quit:
a
Enter a PO number to add: 1
Please enter A(a) to push , P(p) to pop, Q(q) to quit:
a
Enter a PO number to add: 2
Please enter A(a) to push , P(p) to pop, Q(q) to quit:
a
Enter a PO number to add: 3
Please enter A(a) to push , P(p) to pop, Q(q) to quit:
a
Enter a PO number to add: 4
Please enter A(a) to push , P(p) to pop, Q(q) to quit:
a
Enter a PO number to add: 5
Please enter A(a) to push , P(p) to pop, Q(q) to quit:
p
PO #5 poped
Please enter A(a) to push , P(p) to pop, Q(q) to quit:
p
PO #4 poped
Please enter A(a) to push , P(p) to pop, Q(q) to quit:
p
PO #3 poped
Please enter A(a) to push , P(p) to pop, Q(q) to quit:
p
PO #2 poped
Please enter A(a) to push , P(p) to pop, Q(q) to quit:
p
PO #1 poped
Please enter A(a) to push , P(p) to pop, Q(q) to quit:
p
stack already empty
Please enter A(a) to push , P(p) to pop, Q(q) to quit:
q
Bye~

在使用類的程式中,我們包含了cctype這個標頭檔案,它是cctype(字元處理庫)中定義了有關字元判斷與處理的庫函式。以下是一些常用的方法:

方法 說明
isalpha() 判斷一個字元是否是字母(a-z 或 A-Z)
isdigit() 判斷一個字元是否是數字(0-9)。
isalnum() 判斷一個字元是否是字母或數字(a-z、A-Z 或 0-9)。
islower() 判斷一個字元是否是小寫字母(a-z)。
isupper() 判斷一個字元是否是大寫字母(A-Z)。
isspace() 判斷一個字元是否是空白字元(空格、製表符、換行符等)。
isblank() 判斷一個字元是否是空白字元(空格或製表符)。
ispunct() 判斷一個字元是否是標點字元。
isprint() 判斷一個字元是否是可列印字元(非控制字元)。
iscntrl() 判斷一個字元是否是控制字元(非列印字元)。
toupper() 將一個字元轉換為大寫字母。
tolower() 將一個字元轉換為小寫字母。

注意,字元處理庫cctype的方法中每次只能處理一個字元。因此,我們可以透過isalpha()來判斷使用者選擇使用哪個功能,鍵入輸入是'A'/'a','P'/'p',還是'Q'/'q',但我們不能透過isdigit()來判斷100是否是數字。

相關文章