1. 程式人生 > >C++ primer 讀書筆記 第七章 01 定義抽象資料型別

C++ primer 讀書筆記 第七章 01 定義抽象資料型別

定義成員函式

成員函式的宣告必須在類的內部,它的定義既可以在類的內部也可以在類的外部。

struct Sales_data  
{  
    std::string isbn() const { return bookNo; }  
    double arv_price() const;  
};
  • isbn函式的定義和宣告都在類的內部
  • arv_price函式只在類的內部做了宣告,需要在其它地方進行實現

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

this

total.isbn();

我們使用點運算子來訪問total物件的isbn成員,然後呼叫它

當isbn返回bookNo時,實際上它隱式地返回total.bookNo

那麼成員函式是如何訪問呼叫它的物件的呢?

類的成員函式通過一個名為this的額外的隱式引數來訪問呼叫它的那個物件

當我們呼叫一個成員函式時,用請求該函式的物件地址初始化this

這個操作是隱式進行的,this形參也是隱式的

this是一個常量指標

const成員函式

this指標雖然是隱式定義的,但是它也需要遵循初始化規則。

預設情況下,this是指向類的非常量物件的常量指標。

因此,我們無法將類的常量物件的地址賦值給this指標(無法避免通過this修改物件成員的值)。這和普通指標的賦值規則是一致的。

在isbn的實現中,它也只是讀取了資料成員的值,並未對成員進行任何修改。因此它是可以被類的常量物件呼叫的。

基於以上原因,如果將隱式的this形參宣告為指向常量物件的常量指標,會大大提高isbn函式的適用範圍。(c++允許將非常量賦值給指向常量的指標)

但是this是隱式的,因此通過在引數列表後新增const關鍵字來完成這個宣告。

一旦給成員函式添加了const宣告,則在函式體內只能訪問成員,不能修改成員的值。

如果成員被宣告成常量成員函式,那麼它的定義也必須在引數列表後明確指定const屬性

double Saled_data::avg_price() const  
{  
    return units_sold;  
}

這裡的const不能省略

在類外定義成員函式時,帶上類名和作用域運算子是很有必要的,一旦編譯器看到這個函式名,就能理解剩餘的程式碼是位於類的作用域內

返回this物件

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

該函式是在模仿運算子+=,而內建的賦值運算子把它的左側運算物件當成左值返回,為了和它保持一致,combine函式返回引用型別

total.combine(trans);

返回的是total的引用

定義類相關的非成員函式

類的設計者常常需要定義一些輔助函式,儘管這些函式定義的操作從概念上來說屬於類的介面的一部分,但它們實際並不屬於類本身。

這裡需要注意的是,這些非成員函數出現的位置

struct Sales_data  
{  

};  

Sales_data add(const Sales_data&, const Sales_data&);  
std::istream &read(std::istream&, Sales_data&);

建構函式

  • 每個類都分別定義了它的物件被初始化的方式
  • 由一個或多個建構函式來控制物件的初始化過程
  • 無論何時只要類的物件被建立,就會執行建構函式

建構函式是一種特殊的成員函式

  • 它的名字和類名相同
  • 沒有返回型別
  • 不能被宣告成const的

當我們建立類的一個const物件時,直到建構函式完成初始化過程,物件才能真正取得其”常量”屬性。因此,建構函式在const物件的構造過程中可以向其寫值。

形如,

Sales_data total;  
Sales_data trans;

的物件定義過程,我們沒有在定義物件時為其提供初始值,它們執行的是預設初始化。

類通過一個特殊的建構函式來控制預設初始化的過程,稱為預設建構函式

預設建構函式無須任何實參

如果我們的類沒有顯式地定義建構函式,編譯器就會為我們隱式地定義一個預設建構函式。

由編譯器建立的建構函式稱為合成的建構函式。

在下列情況下,最好能提供預設建構函式

  1. 只有當類沒有宣告任何建構函式時,編譯器才會自動地生成預設建構函式。因此,如果我們為類定義了一些其他建構函式,最好也能提供預設建構函式,否則類沒有預設建構函式。
  2. 合成的預設建構函式可能執行錯誤的操作。比如預設初始化資料成員時,可能得到未定義的值
  3. 如果類中包含其它類型別的成員,且這個成員的型別沒有預設建構函式,我們必須自定義預設建構函式,否則該類沒有可用的預設建構函式。
strcut Sales_data  
{  
    Sales_data() = default;  
    Sales_data(const std::string &s):bookNo(s){}  
};

=default

這裡我們定義了其他建構函式,因此編譯器不會為我們生成合成的預設建構函式。

所以自定義了一個預設建構函式,且在它的後面添加了=default關鍵字

=default要求編譯器生成預設建構函式,使用和合成的預設建構函式一樣的規則來初始化成員。

=default可以和宣告一起出現,也可以和定義一起出現在類的外部。

如果出現在類的內部,則預設建構函式是內聯的。

建構函式初始值列表

Sales_data(const std::string &s):bookNo(s){}

:bookNo(s)這部分函式稱為建構函式初始值列表,它的作用是使用建構函式的實參完成成員的初始化過程。

沒有出現在初始值列表中的成員,也有三個機會完成初始化

  1. 建構函式的函式體
  2. 類內初始值
  3. 預設初始化