第七章-類基礎

newloser 發表於 2022-07-02

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