《C++Primer》讀書筆記(七)類

類的基本思想是數據抽象封裝

數據抽象是一種依賴於接口和實現分離的編程技術;類的接口包括用戶所能執行的操作,類的實現包括類的數據成員、負責接口實現的函數體以及定義類所需的各種是有函數

定義抽象數據類型

設計Sales_data類

Sales——data的接口應該包含以下功能
- 一個isbn成員函數,用於返回對應的isbd編號
- 一個combine成員函數,用於將一個Sales_data對象加到另一個對象上
- 一個名爲add的函數,執行兩個Sales_data對象的加法
- 一個read函數,將數據從istream讀入到Sales_data對象中
- 一個print函數,將Sales_data對象的值輸出到ostream

(1)使用改進的Sales_data類
- 首先,我們看看應該如何使用上面的這些接口函數

Sales_data total;
if(read(cin,total)){
    Sales_data trans;
    while(read(cin,trans)){
        if(total.isbn()==trans.isbn())
            total.combine(trans);
        else{
            print(cout,total)<<endl;
            total=trans;
        }
    }
    print(cout,total)<<endl;
}
else{
    cerr<<"No data?!"<<endl;
}

定義改進的Sales_data類

struct Sales_data{

    std::string isbn() const {return bookNo;}
    Sales_data& combine(const Sales_data&);
    double avg_price() const;

    std::string bookNo;
    unsigned units_sold=0;
    double revenue=0;
};
Sales_data add(const Sales_data&,const Sales_data&);
std::ostream &print(std::ostream&,const Sales_data&);
std::istream &read(std::isteam&,Sales_data&);

(1)定義成員函數

(2)引入this
- 實際上,調用一個成員函數,是在爲某個對象調用它。
- 在成員函數 內部,任何類成員的訪問都被看作this的隱式引用,直接使用bookNo,形同於this->bookNo

(3)引入const成員函數
- const的作用是修改隱式this指針的類型
- C++的做法是允許把const關鍵字放在成員函數的參數列表之後,此時,緊跟在參數列表後面的const表示this是一個指向常量的指針。像這樣使用const的成員函數被稱作常量成員函數

常量對象,以及常量對象的引用或指針都只能調用常量成員函數

(4)類作用域和成員函數

(5)在類的外部定義成員函數

double Sales_data::avg_price() const{
    if(units_sold)
        return revenue/units_dold;
    else
        return 0;
}
  • 一旦編譯器看到改函數名,就知道其剩餘代碼是位於類的作用域內的

(6)定義一個返回this對象的函數
- 函數combine的設計初衷類似於複合賦值+=,調用改函數的對象代表賦值運算符左側對象,右側對象則通過顯式的實參被傳入函數

Sales_data& Sales_data::combine(const Sales_data &rhs){
    units_sold+=rhs.units_sold;
    revenue+=rhs.revenue;
    return *this;
}

當我們的交易處理程序調用如下的函數時,

total.combine(trans);

total的地址被綁定到隱式的this參數上,而rhs綁定到了trans上

定義類相關的非成員函數

一般來說,如果非成員函數是類接口的組成部分,則這些函數的聲明應該與類在同一個頭文件內

(1)定義read和print函數

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;
}
  • 使用IO類引用是因爲其IO類屬於不能被拷貝的類型,因此我們只能通過引用來傳遞它們

(2)定義add函數

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

構造函數

(1)合成的默認構造函數

(2)某些類不呢依賴於合成的默認函數

(3)定義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{ retrun bookNo;}
    Sales_data& combine(const Sales_data&);
    double avg_price() const;
    std::string bookNo;

    unsigned units_sold=0;
    double revenue=0.0;
};

(4)=default的含義

(5)在類的外部定義構造函數

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

拷貝、賦值和折構

訪問控制與封裝

class Sales_data{
public:
    Saels_data()=default;
    Sales_data(const std::string &s.unsigned n,double p):bookNo(s),units_sold(n),revenue(p*n) { }
    Sales_data(const std::stirng &s):bookNo(s) { }
    std::string isbn() const { return bookNo; 
    Sales_data &combine(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;
};

(1)使用class或struct關鍵字
- class默認權限爲private
- struct默認權限爲public

友元

  • 類可以允許其他類或者函數訪問它的非公有成員,方法是令其他類或者函數成爲它的友元
class Sales_data{
    friend Sales_data add(const Sales_data&,const Sales_data&);
    friend std::istream &read(std::istream&,Sales_data&);
    friend std:ostream &print(std::ostream&,const 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(std::istream&);
    Sales_data(const std::stirng &s):bookNo(s) { }
    std::string isbn() const { return bookNo; 
    Sales_data &combine(const Sales_data&);
private:
    std::string bookNo;
    unsigned units_sold=0;
    double revenue=0.0;
};
Sales_data add(const Sales_data&,const Sales_data&);
std::istream &read(std::istream&,Sales_data&);
std::osteam &print(std::ostream& const Sales_data&);

類的其他特性

類成員再探

(1)定義一個類型成員

(2)Screen類的成員函數

class Screen{
public:
    typedef std::string::size_tyoe pos;
    Screen()=default;

    Screen(pos ht,pos wd,char c):height(ht),width(wd),contents(ht * wd,c){ }
    char get()const{
        return contents[cursor];
    }
    inline char get(pos ht,pos wd) const;
    Screen &move(pos r,pos c);
private:
    pos cursor=0;
    pos height=0,width=0;
    std::string contents;
};

(3)令成員你作爲內聯函數

inline
Screen &Screen::move(pos r,pos c){
    pos row=r*width;
    cursor=row+c;
    return *this;
}
char Screen::get(pos r,pos c) const{
    pos row=r*width;
    return contents[row+c];
}

(4)重載成員函數

(5)可變數據成員
- 一個可變數據成員永遠不會是const,即使它是const對象的成員
- 小例子:我們將給Screen添加一個名爲access_ctr的可變成員,通過它我們可以追蹤每個Screen的成員函數被調用了多少次

class Screen{
public:
    void some_member() const;
private:
    mutable size_t access_ctr;
};
void Screen::some_memeber() const{
    ++access_ctr
}

(6)類數據成員的初始值

class Window_mgr{
    private:
    std::vector<Screen> screens{Screen(24,80,' ')};
}

當我們提供一個類內初始值時,必須比符號=或者花括號表示

返回*this的成員函數

class Screen {
public:
    Screen &set(char);
    Screen &set(pos,pos,char);
};
inline Screen &Screen::set(char c){
    contents[cursor]=c;
    return *this;
}
inline Screen &Screen::set(pos r,pos col,char ch){
    contents[r*width+col]=ch;
    return *this;
}
  • set成員返回值時調用set的對象的引用
  • 返回引用的函數是左值的,意味着這些函數返回的是對象本身而非副本

把一系列這樣的操作鏈接在一起:

myScreen.move(4,0).set('#');
//這些操作將在同一個對象上執行`
  • 如果我們令move和set返回Screen而非Screen&,則上述語句的行爲將大不相同(相當於對返回值進行拷貝,然後不改變myScreen)

(1)從const成員函數返回*this

一個const成員函數如果以應用的形式返回*this,那麼他的返回類型將是一個常量引用

Screen mySCreen;
// 如果display返回常量引用,則調用set將引發錯誤
myScreen.display(cout).set('*');

(2)基於const的重載

建議:對於公共代碼使用私有功能函數

類類型

  • 每個類定義了唯一的類型。對於兩個類來說,即使它們的成員完全一樣,這兩個類也是兩個不同的類型

(1)類的聲明

友元再探

(1)類之間的友元關係

class Screen{
    friend class Window_mgr;
}
class Window_mgr{
public:
    using ScreenIndex=std::vector<screen>::size_tyoe;
    void clear(ScreenIndex);
private:
    std::vector<Screen> screens{Screen(24,80,'')};
};
void Window_mgr::clear(ScreenIndex i){
    Screen &s=screens[i];
    s.centents=string(s.height * s.width,' ');
}

(2)令成員函數作爲友元

class Screen{
    friend void Window_mrg::clear(ScreenIndex);
}

(3)函數重載和友元

(4)友元聲明和作用域

類的作用域

(1)作用域和定義在類外部的成員

發佈了34 篇原創文章 · 獲贊 5 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章