1. 程式人生 > >7.3 類的其他特性

7.3 類的其他特性

型別成員

class Screen  
{  
public:  
    typedef std::string::size_type pos;  
private:  
    pos cursor = 0;  
    pos height = 0, width = 0;  
    std::string contents;  
};
  • 型別成員同樣存在訪問限制,可以是public或private的
  • 用來定義型別的成員必須先定義後使用。因此,型別成員通常出現在類開始的地方
  • 增加型別成員有兩個好處,隱藏細節、一改全改
  • 也可以使用等價的using宣告

建構函式

Screen類用來表示我們熟悉的命令列視窗,想一想命令列視窗有哪些屬性

  • 視窗的大小(寬、高)
  • 游標的位置
  • 視窗中需要顯示的內容

把Screen類的資料成員和命令列視窗的屬性一一對應後,會更好的理解Screen類

class Screen  
{  
public:  
    Screen() = default;  
    Screen(pos ht, pos wd, char c) : height(ht), width(wd), contents(ht * wd, c) {}  
};

為了方便使用者在建立時指明視窗的屬性值,我們為Screen類添加了一個建構函式,它接受三個引數,分別表示視窗的寬、高和預設顯示的內容

注意到建構函式中並沒有為cursor指定初始值,它將使用類內初始值

行內函數

定義在類內部的成員函式是自動inline的

class Screen  
{  
public:  
    char get() const { return contents[cursor]; }  
    inline char get(pos ht, pos wd) const;  
    Screen &move(pos r, pos c);  
};
  • 無參的get函式是隱式內聯的
  • 帶參的get函式是顯式內聯的
  • move函式仍有機會在定義時新增inline關鍵字修飾

不需要在函式宣告和定義的地方同時說明inline

inline成員函式也應該與相應的類定義在同一個標頭檔案中

可變資料成員

我們知道在const成員函式內,無法修改資料成員的值,但有的時候確實需要。

可以用可變資料成員來解決在const成員函式中無法修改資料成員的問題

在資料成員的宣告中加入mutable關鍵字即可將其宣告為可變資料成員

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

一個可變資料成員永遠不會是const,即使它是const物件的成員。

void Screen::some_member() const  
{  
    ++access_ctr;  
}

類內初始值

當我們為類的資料成員提供類內初始值時,必須以=或者花括號表示。

不能用()表示,因為可能會出現這樣的情況:

class Widget 
{
private: 
  typedef int x;
  int z(x);
};

這樣的話,就會變為函式宣告。

有關初始化的內容,如{} () = 等可以參考這篇文章:https://zhuanlan.zhihu.com/p/21102748

返回*this的成員函式

class Screen  
{  
public:  
    Screen &set(char);  
    Screen &move(pos r, pos c);  
};  

inline Screen &Screen::set(char c)  
{  
    contents[cursor] = c;  
    return *this;  
}

需要注意的是,set和move的返回值型別都是Screen物件的引用,意味著函式返回的是物件本身而非物件的副本

myScreen.move(4,0).set('#');

這樣才能保證類似的連續操作是在同一個物件上執行的。

如果set和move返回Screen而非Screen&,則返回值將是*this的副本。上面的連續呼叫等價於

Screen temp = myScreen.move(4,0);  
temp.set('#');

從const成員函式返回*this

如果成員函式是const的,那麼它的this指標將是一個指向const的指標

而*this是const物件

如果成員函式返回*this,根據賦值規則,返回值型別應該是常量引用,即const Sales_data&

基於const的過載

通過區分成員函式是否是const的,我們可以對其進行過載

Screen &display(std::ostream &os){}  
const Screen &display(std::ostream &os) const {}

兩個display成員函式構成過載,這和this指標有關

類型別

注意區分類和物件

  • 類用來定義一類事物
  • 物件用來表示該類事物的一個具體例項

比如定義了Person類,可以抽象出人類共有的特徵作為Person類的成員,比如眼睛、手、腳作為資料成員,行走、吃飯、睡覺作為函式成員等,那麼具體到某一個人就是類的物件了

Sales_data item1;  
class Sales_data item1;

以上兩種方式都可以用來建立物件(第2種方式是由c語言繼承而來)

類的宣告

和函式類似,我們也可以僅宣告類而暫時不定義它,這種宣告稱作前向宣告

class Screen;

這個時候,我們只知道Screen是一個類型別,但是不知道它有哪些成員,因此使用的場景是有限制的。只能在類的級別使用,不能涉及成員操作。

一旦一個類的名字出現後,它就被認為是宣告過了(但尚未定義),因此允許包含指向它自身型別的引用或指標

class Link_screen  
{  
    Screen window;  
    Link_screen *next;  
    Link_screen *prev;  
};

友元再探

類除了可以把普通函式作為友元外,還可以

  • 把其他的類定義成友元
  • 把其他類的成員函式定義成友元

令類作為友元

class Screen  
{  
    friend class Window_mgr;  
};

作出聲明後,Window_mgr類的成員函式將可以訪問Screen類的所有成員。

void Window_mgr::clear(ScreenIndex i)  
{  
    Screen &s = screens[i];  
    s.contents = string(s.height * s.width, ' ');  
}

令成員函式作為友元

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

宣告本身很簡單,但是必須組織好程式的結構以滿足依賴關係

  • 首先定義Window_mgr類,其中宣告clear函式,但是不能定義它。在clear使用Screen的成員之前必須先宣告Screen
  • 接下來定義Screen,包括對於clear的友元宣告
  • 最後定義clear,此時它才可以使用Screen的成員

 參考P252。