第七章 類(class)7.1 筆記
最好不要把物件的定義和類的定義放在一起,這麼做無異於把兩種不同實體的定義混在了一條語句裡,一會定義類,一會又定義變數,顯然這是一種不被建議的行為。
類的定義最後要加上分號(:)
成員函式的宣告必須放在類的內部,它的定義則既可以在類的內部也可以在類的外部,而作為介面組成部分的非成員函式,例如add、 read、和print等,他們的定義和宣告都在外部
任何對類成員的直接訪問都被看做this的隱式引用,也就是說,當isbn使用bookNo時,它隱式地使用this指向的成員,就像我們書寫了this -> bookNo一樣。this不能用作任何形參名和變數名。
std::string isbn() const { return this -> bookNo ;}
//因為this的目的總是指向這個物件,所以this是一個常量指標。緊跟在引數列表後面的const 表示this是一個指向常量的指標。像這樣使用const的成員函式被稱作常量成員函式(指明這個函式不會修改這個類的任何資料成員的值)。
可以把isbn()函式想象成如下形式
//虛擬碼,不能顯示的定義this指標
std::string Sales_data::isbn(const Sales_data *const this) const { return this -> bookNo ;}
常量物件和常量物件的引用或指標都只能呼叫常量成員函式
編譯器分兩步處理類:首先編譯成員的宣告,然後才輪到成員函式體(如果有的話)。因此,成員函式體可以隨意使用類中的其他成員而無需在意這些成員出現的次序。
像其它函式一樣,當我們在類外部定義成員函式時,成員函式的定義必須與它的宣告匹配。也就是說返回型別、引數列表和函式名都得與類內部的宣告保持一致。如果成員被宣告稱常量成員函式,那麼他的定義也必須在引數列表後表明指定const屬性。同樣,類外部定義的成員的名字必須包含它所屬的類名:
double Sales_data::avg_price() const { //Sales_data::加在函式名之前而非返回型別之前
if (units_sold)
return revenue / units_sold;
else
return 0;
}
Sales_data& Sales_data::combine(const Sales_data &rhs) { units_sold += rhs.units_sold; //把rhs的成員加到this物件的成員上 revenue += rhs.revenue; return *this; //返回呼叫該函式的物件 }
當我的交易處理程式呼叫如下的函式時,
total.combine(trans);
total的地址被繫結到隱式地this引數上,而rh繫結到了trans上。
這個函式一個值得關注的部分是它的返回型別和返回語句。一般來說,當我們定義的函式類似於某個內建運算子時,應該令該函式的行為儘量模仿這個運算子。內建的賦值運算子把他的左側運算物件當成左值返回,因此為了與他保持一致性,combine函式必須返回引用型別。所以返回型別為Sales_data&。
int &refVal = ival; //refVal 是ival的引用,是ival的另一個名字
既然Sales_data的資料成員是private的,我們的read、print、和add函式也就無法正常編譯了,這是因為儘管這幾個函式是類的介面的一部分,但他們不是類的成員,類可以允許其他類或者函式訪問它的非公有成員,方法是令其它類或函式成為它的友元(friend),只需要增加一條以friend關鍵字開始的函式宣告語句即可
istream &read(istream &is, Person &item)//編譯無法通過,要在類內部新增函式友元宣告
{
is >> item.strName >> item.strAddress;
return is;
}
friend istream &read(istream &, Person &);//友元宣告
7.14建構函式
建構函式的名字和類名相同。和其他函式不一樣的是,建構函式沒有返回型別;建構函式也有一個(可能為空的)引數列表和一個(可能為空的)函式體。類可以包含多個建構函式,和其他過載函式差不多, 不同的建構函式之間必須在引數數量或引數型別上有所區別。此外,建構函式不能被宣告成const的。
預設建構函式又稱之為合成的預設建構函式。
某些類不能依賴於合成的預設建構函式的情況:
1.只有當類沒有宣告任何建構函式時, 編譯器才會自動地生成預設建構函式
2.如果類包含有內建型別或者複合型別的成員,則只有當這些成員全都被賦予了類內的初始值時,這個類才適用於合成的預設建構函式。(複合型別就是使用其他型別定義的型別,有三種複合型別,引用、指標、陣列),內建型別就是(int、char、double、unsigned 等基本型別,若沒有給這些成員初始值,預設值可能是未定義的)
3.如果類中包含一個其他類型別的成員且這個成員的型別沒有預設建構函式,那麼編譯器就無法初始化該成員,也就無法生成預設的建構函式。對於這樣的類來說,必須自定義預設建構函式。(classname() = default)
=default(預設建構函式)
建構函式初始值列表
Sales_data(const std::string &s) : bookNo(s) { }//未顯示初始化的部分按預設初始化的方式初始化
等價於:
Sales_data(const std::string &s) : bookNo(s), units_sold(0), revenue(0) { }
Sales_data(const std::string &s, unsigned n, double p) : //初始值列表
bookNo(s), units_sold(n), revenue(p * n) { }
等價於
Sales_data(const std::string &s, unsigned n, double p)
{
bookNo = s;
units_sold = n;
revenue = p * n;
}
二者有一些小小的區別,那就是前者是通過初始化的方式,後者是通過賦值的方式
建構函式最好使用類內初始值或初始值列表
過載運算子:
friend ostream &operator << (ostream & os, const Sales_data & rhs);
friend istream& operator >> (istream &is, Sales_data & rhs);