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。