7.3 類的其他特性
除了前面介紹的簡單的類的特性,還有類型成員癌淮、類的成員的類內(nèi)初始值、可變數(shù)據(jù)成員沦补、內(nèi)聯(lián)成員函數(shù)乳蓄、從成員函數(shù)返回*this等。
7.3.1 類成員再探
如果需要定義一對相互關(guān)聯(lián)的類Screen和Window_mgr夕膀。
定義類型成員:除了定義數(shù)據(jù)和函數(shù)成員外虚倒,類還可以自定義某種類型在類中的別名。
class Screen {
public:
typedef string::size_type pos;
private:
pos cursor = 0;
pos height = 0, width = 0;
string contents;
};
Screen類的成員函數(shù):成員函數(shù)需有構(gòu)造函數(shù)和兩個成員函數(shù)产舞。
class Screen {
public:
typedef string::size_type pos;
Screen() = default;
Screen(pos ht,pot 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;
string contents;
};
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];
}
重載成員函數(shù): 與非成員函數(shù)一樣魂奥,成員函數(shù)也可被重載。
可變數(shù)據(jù)成員:如果希望能修改類的某一個數(shù)據(jù)成員易猫,即使是在一個const成員函數(shù)內(nèi)耻煤,也可通過在變量的聲明中加入mutable關(guān)鍵字。一個可變數(shù)據(jù)成員永遠(yuǎn)不會是const准颓,即使它是const對象的成員哈蝇,一個const成員函數(shù)可以改變一個可變成員的值。
class Screen {
public:
void some_member() const;
private:
mutable size_t access_ctr;
};
void Screen::some_member() const
{
++access_ctr;
}
類數(shù)據(jù)成員的初始值:定義好Screen類之后攘已,可以繼續(xù)定義一個窗口管理類并用它表示顯示器上的一組Screen炮赦。這個類將包含一個Screen類的vector。每個元素表示一個特定的Screen贯被,默認(rèn)情況下希望Window_mgr類總是擁有一個默認(rèn)初始化的Screen眼五,最好的方式就是把這個默認(rèn)值聲明成一個類內(nèi)初始值妆艘。
7.3.2 返回*this的成員函數(shù)
class Screen {
public:
Screen& set(char);
Screen& set(pos, pos, char);
};
inline Screen& Screen::set(char)
{
contents[cursor] = c;
return *this;
}
inline Screen& Screen::set(pos r, pos col, char ch)
{
contents[r * width + col] = ch;
return *this;
}
7.3.3 類類型
每個類都定義了唯一的類型,如果兩個類成員完全一樣看幼,那也是兩個不同的類型批旺。
類的聲明:類的聲明和定義也可分開,可以僅僅聲明類而暫時不定義它诵姜,這樣的聲明被稱作前向聲明汽煮。
7.3.4 友元再探
不僅一個函數(shù)可以為類的友元,一個類也可為另一個類的友元棚唆,但是友元不具有傳遞性暇赤。
令成員函數(shù)作為友元:除了令整個類作為一個類的友元以外,還可以只令該類的一個成員函數(shù)作為友元宵凌。
7.4 類的作用域
每個類都會定義自己的作用域鞋囊,在類的作用域之外,可以使用作用域運(yùn)算符來訪問瞎惫。
作用域和定義在類外部的成員:在類的外部定義成員函數(shù)時必須同時提供類名和函數(shù)名溜腐。遇到類名之后,定義的剩余部分就在類的作用域之中瓜喇。
void Window_mgr::clear(ScreenIndex i)
{
Screen &s = screens[i];
s.contents = string(s.height*s.width,' ');
}
但是函數(shù)的返回類型出現(xiàn)在函數(shù)名之前挺益,所以定義在類外部的成員函數(shù),必須先用作用域運(yùn)算符指明返回值時哪個類的成員乘寒。
class Window_mgr{
public:
ScreenIndex addScreen(const Screen&);
};
Window_mgr::ScreenIndex
Window_mgr::addScreen(const Screen &s)
{
screens.push_back(s);
return screens.size()-1;
}
7.4.1 名字查找與類的作用域
名字查找:
- 首先望众,在名字所在塊中尋找其聲明語句,只考慮在名字的使用之前出現(xiàn)的聲明伞辛。
- 如果沒找到烂翰,繼續(xù)查找外層作用域
在類中:
- 首先,編譯成員的聲明
- 直到類全部可見后才編譯函數(shù)體
用類成員聲明的名字查找: 如果在類成員聲明中就使用的返回類型始锚、形參類型刽酱,都必須在使用前確保可見瞧捌。
typedef double Money;
string bal;
class Account{
public:
Money balance() { return bal; }
private:
Money bal;
}
類型名要特殊處理:
一般內(nèi)層作用域可以重新定義外層作用域中的名字,在類中也一樣润文,但是如果類成員使用了外層作用域中的某個名字之后姐呐,則類不能在之后重新定義該名字。
成員定義中的普通塊作用域的名字查找:
- 首先典蝌,在成員函數(shù)中查找該名字的聲明曙砂,只有在函數(shù)使用之前出現(xiàn)的聲明才被考慮
- 如果在成員函數(shù)中未找到,則在類中繼續(xù)查找骏掀,此時類的所有成員都可以被考慮
- 如果類內(nèi)也沒找到鸠澈,則在成員函數(shù)定義之前的作用域內(nèi)繼續(xù)查找柱告。
7.5 構(gòu)造函數(shù)再探
7.5.1 構(gòu)造函數(shù)初始值列表
對于類所構(gòu)建的對象的數(shù)據(jù)成員而言,初始化與賦值有一定區(qū)別笑陈。如果沒有在構(gòu)造函數(shù)的初始值列表中顯式地初始化成員际度,則該成員將在構(gòu)造函數(shù)體之前進(jìn)行默認(rèn)初始化。
但是如果數(shù)據(jù)成員為const或者是引用及為提供默認(rèn)構(gòu)造函數(shù)的類類型時涵妥,必須在構(gòu)造函數(shù)時對其進(jìn)行初始化乖菱。
成員初始化的順序:構(gòu)造函數(shù)初始值列表只能說明用于初始化成員的值,而不限定初始化的具體執(zhí)行順序蓬网。一般順序不太重要窒所,但是如果用一個成員來對另外一個成員進(jìn)行初始化,就要考慮兩個成員的初始化順序帆锋,一般是依據(jù)聲明的順序進(jìn)行初始化吵取。
7.5.2 委托構(gòu)造函數(shù)
一個委托構(gòu)造函數(shù)使用它所屬類的其他構(gòu)造函數(shù)執(zhí)行它自己的初始化過程,或者它把自己的一些職責(zé)委托給了其他構(gòu)造函數(shù)锯厢。
與其他構(gòu)造函數(shù)一樣海渊,委托構(gòu)造函數(shù)也有成員初始值的列表和函數(shù)體。
class Sales_data{
public:
Sales_data(std::string s,unsigned cnt,double price):
bookNo(s),units_sold(cnt),revenue(cnt*price){}
Sales_data(): Sales_data(" ",0,0){}
Sales_data(std::string s):Sales_data(s,0,0){}
Sales_data(std::istream &is):Sales_data(){read(is,*this);}
}
7.5.3 默認(rèn)構(gòu)造函數(shù)的作用
當(dāng)對象被默認(rèn)初始化和或值初始化時會自動執(zhí)行默認(rèn)構(gòu)造函數(shù)哲鸳。默認(rèn)初始化在以下情況下發(fā)生:
- 在塊作用域不使用任何初始值定義一個非靜態(tài)變量或數(shù)組時
- 當(dāng)一個類還有類類型成員且使用合成的默認(rèn)構(gòu)造函數(shù)時
- 當(dāng)類類型成員沒有在構(gòu)造函數(shù)初始值列表中顯式地初始化
值初始化在以下情況發(fā)生:
- 在數(shù)組初始化的過程中如果提供的初始值數(shù)量小于數(shù)組大小時
- 不使用初始值定義一個局部靜態(tài)變量時
所以類必須包含一個默認(rèn)構(gòu)造函數(shù)以便在上述情況下使用臣疑。
7.5.4 隱式的類類型轉(zhuǎn)換
C++語言在內(nèi)置類型之間定義了幾種自動轉(zhuǎn)換規(guī)則,而我們也可為類定義隱式轉(zhuǎn)換規(guī)則徙菠。如果構(gòu)造函數(shù)只接受一個實(shí)參讯沈,那么它實(shí)際上定義了轉(zhuǎn)換為此類型的隱式轉(zhuǎn)換機(jī)制,叫做轉(zhuǎn)換構(gòu)造函數(shù)婿奔。
string null_book="9-999-99";
item.combine(null_book);
在程序中缺狠,編譯器會將string對象null_book隱式轉(zhuǎn)換為Sales_data對象,并將其傳遞給combine萍摊。
但是在隱式類型轉(zhuǎn)換中挤茄,只允許一步的類型轉(zhuǎn)換,如下程序則不能實(shí)現(xiàn)隱式類型轉(zhuǎn)換冰木。
item.combine("9-999-99");//錯誤
item.combine(string("9-999-999"));//正確
item.combine(Sales_data("9-999-99"));//正確
抑制構(gòu)造函數(shù)定義的隱式轉(zhuǎn)換:可以用關(guān)鍵詞explicit加在構(gòu)造函數(shù)之前穷劈,抑制其隱式類型轉(zhuǎn)換的功能。但是仍可以用其實(shí)現(xiàn)顯式類型轉(zhuǎn)換踊沸。
7.5.5 聚合類
聚合類可以讓用戶直接訪問其成員歇终,并具有特殊的初始化語法形式,它有如下條件:
- 所有成員都是public的
- 沒有定義任何構(gòu)造函數(shù)
- 沒有類內(nèi)初始值
- 沒有基類逼龟,也沒有virtual函數(shù)
7.6 類的靜態(tài)成員
有時類需要與其成員直接相關(guān)评凝,而不是與類的各個對象保持關(guān)聯(lián),所以可以用類的靜態(tài)成員來表示腺律。
在成員的聲明前加上關(guān)鍵字static就可使得其與類關(guān)聯(lián)在一起奕短,和其他成員一樣宜肉,靜態(tài)成員可以是public的或private的,且可以是常量翎碑、引用谬返、指針、類類型等
靜態(tài)成員函數(shù)也不與任何對象綁定在一起杈女,它們沒有this指針朱浴,且不能聲明成const的。