1. 程式人生 > >《C++Primer 5e》學習筆記(6):類

《C++Primer 5e》學習筆記(6):類

#include <iostream>
using namespace std;


struct Sales_data
{
    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()=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 &);

    string isbn() const {return bookNo;}
    Sales_data& combine(const Sales_data&);
private:

    double avg_price() const ;
    string bookNo; //表示ISBN號
    unsigned units_sold=0; //某本書的銷量
    double revenue=0.0; //這本書的總銷售收入
};

Sales_data add(const Sales_data&,const Sales_data&);
ostream &print(ostream &,const Sales_data&);
istream &read(istream&,Sales_data&);

Sales_data::Sales_data(istream &is)
{
    read(is,*this); //從is中讀取一條資訊然後存入this中
}

double Sales_data::avg_price() const
{
    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; //返回呼叫該函式的物件
}

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();
}

Sales_data add(const Sales_data &lhs,const Sales_data &rhs)
{
    Sales_data sum=lhs;
    sum.combine(rhs);
    return sum;
}

int main()
{
    Sales_data total; //儲存當前求和結果的變數
    Sales_data trans; //儲存下一條交易資料的變數
    total.combine(trans); //返回total的引用

    return 0;
}


1.定義在類內部的函式是隱式的inline函式

2.成員函式通過this隱式引數來訪問呼叫它的那個物件。當我們呼叫一個成員函式時,用請求該函式的物件的地址去初始化this。

3.this的目的是總指向“這個”物件,所以預設情況下this的型別是指向類型別非常量版本的常量指標,我們不允許改變this中儲存的地址。這也意味著this不能繫結到一個常量物件上,從而導致我們不能在一個常量物件上呼叫普通的成員函式。C++允許我們把const關鍵字放在成員函式的引數列表後,以緊跟在引數列表之後的const來表示this指標是一個指向常量的指標:

std::string isbn() const 
{
    return bookNo;//等價於return this->bookNo
}


4.編譯器分兩步處理類:首先編譯成員的宣告,然後才輪到成員函式體。因此,成員海曙可以隨意使用類中的其他成員而無須在意這些成員出現的次序。

5.類外部定義的成員必須包含他所屬的類名:

double Sales_data::avg_price const (){ /*..*/} //Sales_date為類名


6.

Sales_data& Sales_data::combine(const Sales_data &rhs)
{
    units_sold+=rhs.units_sold; //把rhs的成員加到this物件的成員上
    revenue+=rhs.revenue;
    return *this; //返回呼叫該函式的物件
}

Sales_data total,trans;
total.combine(trans); //返回total的引用


我們無須使用隱式的this指標訪問函式呼叫者的某個具體成員,而是需要把呼叫函式的物件當成一個整體來訪問;return語句解引用this指標以獲得執行該函式的物件。PS:返回引用的函式是左值,意味著這些函式返回的是物件本身而不是物件的副本。

一個const成員函式如果以引用的形式返回*this,那麼它的返回型別將是常量引用。

7.建構函式的任務是初始化類物件的資料成員,無論何時只要類的物件被建立,就會執行建構函式。不同於其他函式,建構函式不能被宣告成const的。當我們建立類的一個const物件時,直到建構函式完成初始化過程,物件才能真正取得其“常量”屬性。因此,建構函式在const物件的構造過程中可以向其寫值。

8.合成的預設建構函式(由編譯器自動生成的的)的初始化規則:如果雷記憶體在初始值,用它來初始化成員;否則,預設初始化。PS:編譯器只有在發現類不包含任何建構函式的情況下才會替我們生成一個預設的建構函式。

9.每個訪問說明符制定了接下來的成員的訪問級別,其有效範圍直到出現下一個訪問說明符或者到達類的結尾處為止。

10.關鍵字class關鍵字struct定義類唯一的區別就是預設的訪問許可權不同:class則定義在第一個訪問說明符之前的成員是private的;而用struct的話,這些成員是public的。

11.友元宣告只能出現在類定義的內部,但是在類內出現的具體位置不限。友元不是類的成員函式,也不受它所在區域訪問控制級別的約束。注意:友元關係不存在傳遞性。

如果一個類想要把一組過載函式宣告為它的友元函式,它需要對這組過載函式中的每一個分別宣告。

12.可變資料成員(mutable)永遠不會是const,即使它是const物件的成員。因此,一個const成員函式可以改變一個可變成員的值。

13.我們可以只宣告一個類而不去定義它:

class Sales_data; //Sales_data類的宣告


但在我們建立一個類的物件之前該類必須被定義過,類似的,類也必須首先被定義,然後才能用引用或指標訪問其成員。即:只宣告不定義類,可以定義指向它的引用或指標,但不能用引用或指標訪問類。

14.編譯器處理完類中全部的宣告之後才會處理成員函式的定義。

15.如果成員是const、引用、或者屬於某種未提供預設建構函式的類型別,我們必須通過建構函式初始值列表為這些成員提供初始值。PS:我們初始化const或者引用型別的資料成員的唯一機會就是通過建構函式初始值。

16.建構函式初始值列表只是說明了用於初始化成員的值,而不限定初始化的具體執行順序。

17.如果一個建構函式為所有引數都提供了預設實參,則它實際上也定義了預設建構函式。

18.能通過一個實參呼叫的建構函式定義了一條從建構函式的引數型別向類型別隱式轉換的規則。PS:該規則只允許一步類型別轉換。

string null_book="9-999-9999-9";
//構造一個臨時的Sales_data物件
//該物件的units_sold和revenue等於0,bookNo等於null_book
item.combine(null_book); 

//錯誤:需要使用者定義兩步轉換:
//(1).把"9-999-9999-9"轉換成string
//(2)再把這個臨時的string轉換成Sales_data
item.combine("9-999-9999-9");


19.通過將建構函式宣告為explicit可以阻止類型別的隱式轉換:只能在類內宣告建構函式時使用關鍵字explicit,在類外部定義時不應重複。explicit只對一個實參的建構函式有效(因為隱式轉換隻能通過有一個實參的建構函式呼叫)。PS:explicit建構函式只能用於直接初始化,而不能用於賦值初始化:

class Sales_data
{
    explicit Sales_data(const string &s):bookNo(s){}
};

Sales_data item1(null_book); //正確:直接初始化
Sales_data item2=null_book //錯誤:不能將explicit建構函式用於拷貝形式的初始化


20.通過關鍵字static可以將成員宣告為靜態成員,靜態成員與類關聯在一起。靜態成員函式不與任何物件繫結在一起,不含this指標,不能宣告成為const的。類可以使用作用域運算子來訪問靜態成員:類名::靜態成員,成員函式不用通過作用域運算子就能直接使用靜態成員。

21.static只能在類內部宣告。由於靜態成員不屬於類的任何一個物件,所以他們並不是在建立類的物件時被定義的,這意味著他們不是由建構函式初始化的。一般來說,我們不能在類的內部初始化靜態成員,必須在類的外部定義和初始化每個靜態成員。類似於全域性變數,靜態資料成員定義在任何函式之外,因此一旦被定義,就將一直存在於程式的整個生命週期當中。