第七章-類基礎
7.1 定義抽象數據類型
抽象數據類型的最大特點是其具有很高的封裝性,我們無法直接訪問其內部的數據,甚至我們不清楚其內部都有哪些類型的數據,我們僅使用其提供的各種介面(api)來對其數據進行訪問和操作。
C++中的類就是一種抽象的數據類型,類的基本思想就是數據抽象和封裝。
僅由一組數據組成的結構體並不是一種抽象的數據類型,因為我們能直接訪問其內部數據,而不是通過介面訪問。如下的Sales_data
如果想要將書的交易記錄Sales_data定義為一個抽象數據類型,我們需要提供一組操作(介面),每次交易只調用這些操作,這樣就可以將數據封裝起來。
(1)設計Sale_data類
書的交易記錄Sale_data包含的數據有:書的標號 bookNo(定義為 string類對象),書的銷量 units_sold(定義為 unsigned類型)和交易總價 revenue(定義為 double類型),使用這三個數據類型就可以完整的描述每單的交易了;除了對每單交易的描述外,還應提供哪些操作呢?
我們可以設計了這樣一些操作:
a.每單交易的錄入read()和列印該單交易print()
b.獲取每單交易的參數:獲取書編號getBookNo(),獲取銷量getUnitsSold(),獲取銷售總價getRevenue()
c.實現同一本書的多單交易的匯總統計(將一單交易加到另一單上)combine()
程式碼如下:
先編寫頭文件Sales_data ,將Saes_data類定義在頭文件里 //注意,頭文件名需要與類名一致,這是規範各文件名中類定義一致,非語法強制要求。
Sales_data類定義
#ifndef SALES_DATA_H
#define SALES_DATA_H //定義頭文件時必須加保護,防止其他.cpp重複引入此頭文件。
#include <iostream>
#include <string>
using namespace std;
//統計每單書的交易記錄
struct Sales_data {
//數據成員
string bookNo; //書的編號
unsigned units_sold = 0; //銷量
double revenue = 0; //銷售總價
//成員函數 必須在類或結構體內聲明,可以在外面定義,但需要在函數名前加 類名::(Sales_data::)
string getBookNo();
unsigned getUnitsSold();
double getRevenue();
void read();
void print();
void combine(Sales_data next);
};
string Sales_data::getBookNo() {
return bookNo;
}
unsigned Sales_data::getUnitsSold() {
return units_sold;
}
double Sales_data::getRevenue() {
return revenue;
}
void Sales_data::read() {
cout << "請輸入一條交易" << endl;
cout << "書編號:";
cin >> bookNo;
cout << "銷量:";
cin >> units_sold;
cout << "銷售總價";
cin >> revenue;
}
void Sales_data::print() {
cout << "編號:" << bookNo << " 賣出" << units_sold << "本" << " 總價:" << revenue << endl;
}
void Sales_data::combine(Sales_data next) {
this->units_sold += next.units_sold;
this->revenue += next.revenue;
}
#endif // !SALES_DATA_H
主程式(輸入一批交易)
int main() {
Sales_data toutal, next;
int k=1;
toutal.read();
while (k!=-1)
{
next.read();
if (toutal.getBookNo() == next.getBookNo()) {
toutal.combine(next);
}
else {
toutal.print();
toutal = next;
}
cout << "是否停止錄入? ";
cin >> k;
}
toutal.print();
return 0;
}
通過以上,定義了描述每單交易的必要參數,以及每單交易和多單交易之前可能會用的的操作,這樣,Sale_data類基本就設計好。但還需要打磨一下設計的細節:
需要先介紹兩個概念:
引入this指針:
通過對象.成員函數調用時,形參表裡會隱式的傳入一個指向該對象的常量指針this,實際上在成員函數內使用對象的數據成員時,是隱式的使用this.數據成員.
this指針始終都指向調用對象,所以this都是常量指針 (type * const類型)
如toutal.getBookNo();將隱式傳入 this常量指針,存放的是toutal對象的地址(Sales_data *const this=&toutal;)。
引入const成員函數
如果調用對象是一個常量對象時,默認的指針類型是不能指向一個常量對象的,所以需要指定this指針為指向常量的指針,只需要在定義和聲明成員函數時,在形參列表後添加關鍵字const,用以修飾this為指向常量的指針常量。
使用const的成員函數稱為常量成員函數,無論是常量對象還是非常量對象都可以調用它,但它只能讀取調用對象的數據成員,無權修改調用對象。 (常量對象以及其引用或指針只能調用它的常量成員函數)
知道以上兩個概念後,我們可以將getBookNo()等成員函數定義為常量成員函數,如 string Sales_data::getBookNo() const{…}
因為getBookNo()只讀取對象的數據成員,無修改對象的操作。
類的設計者負責思考描述一個類事物需要哪些參數,然後將這些參數進行封裝,並設計一些相關操作,從而得到這類事務的一個模板;如描述人類,需要性別,姓名,年齡等特徵參數,然後我們可以設計一個走路的操作,統計他一分鐘走多遠等等,這樣就設計了一個簡單的human模板,可以通過此模板創建一個個具體的人類對象張三或者李四。
而類的User(即類的調用者),不應該去過多思考類的實現過程,甚至不需要了解它都有哪些數據成員和成員函數,我們僅需思考這個類型可以做些什麼?然後直接用其提供的API(成員函數)。