第七章-類基礎

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(成員函數)。