本文章是作者根據史蒂芬·普拉達所著的《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是否是數字。