類
在c++中珊燎,可以使用類定義自己的數(shù)據(jù)類型。類的基本思想是數(shù)據(jù)抽象和封裝粟焊。數(shù)據(jù)抽象是一種依賴于接口和實現(xiàn)分離的編程技術(shù)。
類的接口包括用戶所能執(zhí)行的操作担锤;類的實現(xiàn)規(guī)則包括類的數(shù)據(jù)成員、負(fù)責(zé)接口實現(xiàn)的函數(shù)體以及定義類所需的各種私有函數(shù)滤奈。
封裝實現(xiàn)了類的接口和實現(xiàn)的分離葵礼。封裝后隱藏了類的實現(xiàn)細(xì)節(jié)粗梭。
7.1 定義抽象數(shù)據(jù)類型
抽象數(shù)據(jù)類型不允許用戶直接訪問它的數(shù)據(jù)成員罩扇。
7.1.1 設(shè)計Sales_data類
Sales_data類的數(shù)據(jù)成員包括:
- string類型的bookNo剿干,表示ISBN編號蜂怎;
- unsigned類型的units_sold穆刻,表示某本書的銷量置尔;
- double類型的revenue,表示這本書的總銷售輸入氢伟;
Sales_data類的接口應(yīng)該包含以下操作:
- 一個isbn成員函數(shù)榜轿,用于返回對象的ISBN編號;
- 一個combine成員函數(shù)朵锣,用于將一個Sales_data對象加到另一個對象上谬盐;
- 一個名為add的函數(shù),執(zhí)行兩個Sales_data對象的加法诚些;
- 一個read函數(shù)飞傀,將數(shù)據(jù)從istream讀入到Sales_data對象中;
- 一個print函數(shù)诬烹,將Sales_data對象的值輸出到ostream中砸烦;
7.1.2 定義改進的Sales_data類
定義類的時候,需要在類函數(shù)體內(nèi)定義數(shù)據(jù)成員绞吁、成員函數(shù)和非成員接口函數(shù)幢痘。其中成員函數(shù)的聲明必須在類的內(nèi)部,定義在類的內(nèi)部和外部都可家破。而作為接口組成部分的非成員函數(shù)颜说,聲明和定義都在類的外部。
struct Sales_data {
//數(shù)據(jù)成員
string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
//成員函數(shù)
string isbn() const { return bookNo; };
Sales_data& combine(const Sales_data&);
double avg_price() const;
};
//Sales_data的非成員函數(shù)
Sales_data add(const Sales_data&, const Sales_data&);
ostream& print(ostream&, const Sales_data&);
istream& read(istream&, Sales_data&);
this:對于成員函數(shù)isbn()汰聋,它只返回了類的bookNo數(shù)據(jù)成員门粪,實際上返回的是當(dāng)前對象的數(shù)據(jù)成員。
const成員函數(shù):isbn函數(shù)的參數(shù)列表之后的const關(guān)鍵字烹困,其作用是修改隱式this指針的類型玄妈。在參數(shù)列表后的const表示this是一個指向常量的指針,這樣使用const的成員函數(shù)被稱為常量成員函數(shù)韭邓。
在類的外部定義成員函數(shù)措近,其中Sales_data::avg_price是作用域運算符,表示avg_price函數(shù)是屬于類的作用域內(nèi)女淑。
double Sales_data::avg_price() const {
if (units_sold)
return revenue / units_sold;
else
return 0;
}
定義一個返回this對象的函數(shù):
Sales_data& Sales_data::combine(const Sales_data& rhs) {
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
7.1.3 定義類相關(guān)的非成員函數(shù)
類的作者常常需要一些輔助函數(shù)瞭郑,盡管這些函數(shù)定義的操作從概念上來說屬于類的接口的組成部分,但是實際上不屬于類本身鸭你。
定義read和print函數(shù):
istream& read(istream& is, Sales_data& item)
{
double price = 0;
is >> item.bookNo >> item.units_sold >> price;
item.revenue = price * item.units_sold;
return is;
}
ostream& print(ostream& os, const Sales_data& item)
{
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.avg_price();
return os;
}
定義add函數(shù):
Sales_data add(const Sales_data& lhs, const Sales_data& rhs)
{
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
7.1.4 構(gòu)造函數(shù)
每個類都通過構(gòu)造函數(shù)分別定義了它的對象被初始化的方法屈张,構(gòu)造函數(shù)的任務(wù)是初始化類對象的數(shù)據(jù)成員擒权,無論何時類被創(chuàng)建,都會執(zhí)行構(gòu)造函數(shù)阁谆。
構(gòu)造函數(shù)沒有返回類型碳抄,其形參列表可以為空也可由形參,函數(shù)體也可以為空也可有內(nèi)容场绿。類可以包含多個構(gòu)造函數(shù)剖效,和其他重載函數(shù)類似。
合成的默認(rèn)構(gòu)造函數(shù):如果一個類并沒定義任何構(gòu)造函數(shù)焰盗,則類通過一個特殊的構(gòu)造函數(shù)來控制默認(rèn)初始化過程璧尸,叫做默認(rèn)構(gòu)造函數(shù),由編譯器創(chuàng)建的構(gòu)造函數(shù)叫做合成的默認(rèn)構(gòu)造函數(shù)熬拒,如果存在類內(nèi)初始值就用它來初始化成員爷光,否則默認(rèn)初始化該成員。
合成的默認(rèn)構(gòu)造函數(shù)只適合非常簡單的類澎粟,一般都得定義自己的默認(rèn)構(gòu)造函數(shù)蛀序。默認(rèn)構(gòu)造函數(shù)即以自身的類名作為函數(shù)名。
//構(gòu)造函數(shù)
Sales_data() = default;
Sales_data(const string &s):bookNo(s){}
Sales_data(const string &s ,unsigned n, double p):
bookNo(s),units_sold(n),revenue(p*n){}
Sales_data(istream&);
Sales_data() = default:該構(gòu)造函數(shù)不接受任何實參活烙,所以它是個默認(rèn)構(gòu)造函數(shù)徐裸,定義該構(gòu)造函數(shù)的目的僅僅是因為需要其它形式的構(gòu)造函數(shù)。
第二和第三個構(gòu)造函數(shù)中出現(xiàn)了新的部分:冒號和花括號之間的代碼瓣颅,該部分被稱為構(gòu)造函數(shù)初始值列表倦逐,其中花括號定義了空的函數(shù)體,負(fù)責(zé)為新創(chuàng)建的對象的一個或幾個數(shù)據(jù)成員賦初值宫补。
第三個構(gòu)造函數(shù)的定義在類的外部檬姥。定義構(gòu)造函數(shù)時必須指明該構(gòu)造函數(shù)時哪個類的成員。
調(diào)用各構(gòu)造函數(shù)的代碼如下:
Sales_data total;
Sales_data t1("10001-100");
Sales_data t2("10001-100",5,30.5);
Sales_data t3(cin);
7.1.5 拷貝粉怕、賦值和析構(gòu)
除了類的對象初始化之外健民,還需要控制拷貝、賦值和銷毀對象時發(fā)生的行為贫贝。如果沒有主動定義這些操作秉犹,編譯器將合成它們。比如沒有定義賦值語句稚晚,就會將各個數(shù)據(jù)成員進行賦值崇堵。
7.2 訪問控制與封裝
在C++中,可以使用訪問說明符來加強類的封裝性客燕。
- 定義在public說明符之后的成員在整個程序內(nèi)可被訪問鸳劳,public成員定義類的接口
- 定義在private說明符之后的成員可以被類的成員函數(shù)訪問,但是不能被使用該類的代碼發(fā)昂我也搓,private封裝了類的實現(xiàn)細(xì)節(jié)
class關(guān)鍵字和struct關(guān)鍵字都可以用來定義類赏廓,但是class關(guān)鍵字在第一個訪問說明符之前的成員都是private的涵紊,而struct關(guān)鍵字在第一個訪問說明符之前的成員都是public的。
7.2.1 友元
如果類的數(shù)據(jù)成員是private的幔摸,則在函數(shù)外定義的類的接口無法正常編譯摸柄。如果想讓其他類或者函數(shù)訪問它的非公有成員,就應(yīng)該令其他類和函數(shù)稱為他的友元既忆,如下:
class Sales_data{
//為類的非成員函數(shù)做的友元聲明
friend Sales_data add(const Sales_data&, const Sales_data&);
friend istream &read(istream&, Sales_data&);
friend ostream &print(ostream&, const Sales_data&);
public:
Sales_data()=defult;
...
private:
string bookNo;
unsigned units_sold=0;
double revenue=0.0;
}