C++ Primer 5th學習筆記6 類

  類的基本思想是數據抽象和封裝,類的接口包括用戶所能執行的操作;類的實現包括類的數據成員、負責接口實現的函數體以及定義類所需的各種私有函數。

1 定義抽象數據類型

 從改進的Sales_data開始,其結構如下圖所示:

struct Sales_data
{
    std::string  isbn() const {return bookNo; }
    Sales_data& conbine(const Sales_data&);
    double avg_price() const;
    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};
//Sales_data的非成員接口函數
Sales_data add(const Sales_data&, const Sales_data&);
std::ostream &print(std::ostream&, const Sales_data&);
std::istream &read(std::istream&, Sales_data&);

1.1 定義成員函數

  成員函數可以通過一個名爲this的額外隱式參數來訪問調用它的那個對象。如下面的isbn函數,可以改爲另外一種方式:

//原函數
std::string  isbn() const {return bookNo; }
//採用this定義
std::string  isbn() const {return this->bookNo; }

isbn函數中參數列表之後的const關鍵字使用是:修改隱式this指針的類型

類的作用域和成員函數
  編譯器在處理類時,有兩步:首先編譯成員的聲明,若有成員函數體的話,纔去編譯成員函數體,因此函數體可以隨意使用類中的其他成員而無須在意這些成員出現的次序

在類的外部定義成員函數
  當在類的外部定義成員函數時,成員函數的定義必須與它的聲明匹配。如果成員函數被聲明成常量成員函數,則其定義也必須在參數列表後明確指定const屬性,同時,類外部定義的成員的名字必須包含它所屬的類名

double Sales_data::avg_price() const
{
    if (units_sold)
        return revenue/units_sold;
    else
        return 0;
    
}

定義一個返回this對象的函數
  函數combine的設計類似於複合賦值運算符±,調用該函數的對象代表的是賦值運算符左側的運算對象,右側運算對象則通過顯式的實參被傳入函數:

Sales_data& Sales_data:;combine(const Sales_data &rhs)
{
    units_sold += rhs.units_sold;       //把rhs的成員加到this對象的成員上
    revenue += rhs.revenus;
    return *this;       //返回調用該函數的對象
}

return語句解引用this指針以獲得執行該函數的對象,即函數調用返回tatal的引用

1.2 定義類相關的非成員函數

定義read和print函數
  其定義程序如下:

//輸入的交易信息包括ISBN、售出總數和售出價格
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;
}

read函數從給定流中將數據讀到給定的對象,print函數則負責將給定內容的對象打印到給定的流中。
Tip:read和print分別手一個各自IO類型的引用作爲其參數,這是因爲IO類屬於不能被拷貝的類型,因此我們只能通過引用來傳遞;由於讀取和寫入的操作會改變流的內容,因此兩個函數接受的都是普通引用。

1.3 構造函數

  構造函數:控制類的對象初始化的函數,作用是初始化類對象的數據成員。構造函數的名字和類名相同;構造函數不能被聲明成const,構造函數在const對象的構造過程中可以向其寫值。
定義Sales_data的構造函數,此時最初的類的程序變爲:

struct Sales_data
{
    //新增的構造函數
    Sales_data() = default;        //聲明默認構造函數
    //構造函數初始值列表
    Sales_data(const std::string &s): bookNo(s) {}
    Sales_data(const std::string &s, unsigned n, double p):
              bookNo(s), units_sold(n), revenue(p *  n){}
              
    Sales_data(std::istream &);
    //之前已有的其他成員
    std::string  isbn() const {return bookNo; }
    Sales_data& conbine(const Sales_data&);
    double avg_price() const;
    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
    
}

在類的外部定義構造函數
  在類的外部定義構造函數時,必須指明該構造函數是哪個類的成員,示例如下

Sales_data::Sales_data(std::istream &is)
{
     read(is, *this);
}

默認構造函數的作用
 默認初始化在以下情況下發生:

  • 當我們在塊作用域內不使用任何初始值定義一個非靜態變量或數組時;
  • 當一個類本身含有類類型的成員且使用合成的默認構造函數是;
  • 當類類型的成員沒有在構造函數初始值列表中顯示地初始化時;

 值初始化在以下情況下發生:

  • 在數組初始化的過程中若提供的初始值數量少於數組的大小時
  • 當不使用初始值定義一個局部靜態變量時
  • 當通過書寫形如T()的表達式顯式地請求值初始化時,其中T是類型名

2 訪問控制與封裝

  使用訪問說明符加強類的封裝性:

  • 定義在public說明符之後的成員在整個程序內可被訪問,public成員定義類的接口
  • 定義在private說明符之後後的成員可以被類的成員函數訪問,但是不能被使用該類的代碼訪問,private封裝了類的實現細節
    再次定義Sales_data類,其程序如下:
class Sales_data
{
    public:        //添加訪問說明符
        Sales_data() = default;        //聲明默認構造函數
        //構造函數初始值列表
        Sales_data(const std::string &s, unsigned n, double p):
              bookNo(s), units_sold(n), revenue(p *  n){}
        Sales_data(const std::string &s): bookNo(s) {}
        Sales_data(std::istream &);
        std::string  isbn() const { return bookNo; }
        Sales_data& conbine(const Sales_data&);
    private:        //添加訪問說明符
        double avg_price() const
            { return units_sold ? revenue/units_sold : 0;}
        std::string bookNo;
        unsigned units_sold = 0;
        double revenue = 0.0;
};

使用classstruct關鍵字
  struct與class的區別在於默認的訪問權限不同。如果使用的是struct關鍵字,則定義在第一個訪問說明符之間的成員是pubilc的,相反,如果我們使用class關鍵字,則這些成員是private的。

2.1 友元

  類可以允許其他類或者函數訪問它的非公有成員,方法是令其類或函數成爲它的友元,在函數聲明語句的開始之前加上friend關鍵字即可。示例如下:

class Sales_data
{
    //爲Sales_data的非成員函數所做的友元聲明
    friend Sales_data add(const Sales_data&, const Sales_data&);
    friend std::ostream &print(std::ostream&, const Sales_data&);
    friend std::istream &read(std::istream&, Sales_data&);
    public:        //添加訪問說明符
        Sales_data() = default;        //聲明默認構造函數
        //構造函數初始值列表
        Sales_data(const std::string &s, unsigned n, double p):
              bookNo(s), units_sold(n), revenue(p *  n){}
        Sales_data(const std::string &s): bookNo(s) {}
        Sales_data(std::istream &);
        std::string  isbn() const { return bookNo; }
        Sales_data& conbine(const Sales_data&);
    private:        //添加訪問說明符
        double avg_price() const
            { return units_sold ? revenue/units_sold : 0;}
        std::string bookNo;
        unsigned units_sold = 0;
        double revenue = 0.0;
};
//爲Sales_data的非成員函數所做的友元聲明
Sales_data add(const Sales_data&, const Sales_data&);
std::ostream &print(std::ostream&, const Sales_data&);
std::istream &read(std::istream&, Sales_data&);

友元聲明只能出現在類定義的內部

封裝的益處
  封裝有兩個重要的特點:

  • 確保用戶代碼不會無意間破壞封裝對象的狀態
  • 被封裝的類的具體實現細節可以隨時改變,而無須調整用戶級別的代碼。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章