本文目的是實現(xiàn)一個實用的對C++類計數(shù)的類,同時在實現(xiàn)過程中指出一些容易為人忽視的C++知識。
要實現(xiàn)一個類的對象(實例)計數(shù),即程序運行中此類有多少個對象存在,最容易的實現(xiàn)方法是使用靜態(tài)數(shù)據(jù)成員。如下:
?01.
class
Widget {
02.
public
:
03.
Widget() { ++count; }
04.
Widget(
const
Widget&)
{ ++count; }
05.
~Widget() { --count; }
06.
07.
static
size_t
howMany()
08.
{
return
count; }
09.
10.
private
:
11.
static
size_t
count;
12.
};
13.
14.
//cpp文件中
15.
size_t
Widget::count = 0;
注意構(gòu)造函數(shù)也要增加計數(shù),這一點很多人容易忘記。
但是如果程序中有多個需要實例計數(shù)的類,則在每個類中加入上面代碼未免繁瑣、易錯。這種情況下,最好是實現(xiàn)一個通用計數(shù)類。它應(yīng)該具備一下特點:
易于使用:任何需要計數(shù)的類(以下簡稱客戶類)只要添加少數(shù)代碼即可使用;
有效率:不增加客戶類大小,對客戶類性能沒有影響;
健壯:客戶類使用時,不容易誤用。
下面我們將逐步實現(xiàn)并完善這個通用的計數(shù)類。
?01.
class
Counter {
02.
public
:
03.
Counter() { ++count; }
04.
Counter(
const
Counter&)
{ ++count; }
05.
~Counter() { --count; }
06.
static
size_t
howMany()
07.
{
return
count; }
08.
09.
private
:
10.
static
size_t
count;
11.
};
12.
13.
// This still goes in an implementation file
14.
size_t
Counter::count = 0;
上面這個Counter類能否正確完成計數(shù)呢?例如:Widget類利用它來進行實例計數(shù):
?01.
// embed a Counter to count objects
02.
class
Widget {
03.
public
:
04.
.....
// all the usual public
05.
// Widget stuff
06.
static
size_t
howMany()
07.
{
return
Counter::howMany(); }
08.
private
:
09.
.....
// all the usual private
10.
// Widget stuff
11.
Counter c;
12.
};
13.
14.
//or:
15.
16.
// inherit from Counter to count objects
17.
class
Widget:
public
Counter {
18.
.....
// all the usual public
19.
// Widget stuff
20.
private
:
21.
.....
// all the usual private
22.
// Widget stuff
23.
};
對于Widget本身來說,Counter完成了任務(wù)。然而,如果我們在同一進程中還需要利用Counter來計數(shù)Fish類,顯然,Counter就不能勝任,因為它只有一個靜態(tài)成員變量,它會將Widget和Fish的個數(shù)一起統(tǒng)計。這個方案不行,怎么辦?用模板!如下:
?01.
template
02.
class
Counter {
03.
public
:
04.
Counter() { ++count; }
05.
Counter(
const
Counter&)
{ ++count; }
06.
~Counter() { --count; }
07.
08.
static
size_t
howMany()
09.
{
return
count; }
10.
11.
private
:
12.
static
size_t
count;
13.
};
14.
15.
// this now can go in header
16.
template
size_t
Counter::count = 0;
則上面的實現(xiàn)變成:
?01.
// embed a Counter to count objects
02.
class
Widget {
03.
public
:
04.
.....
05.
static
size_t
howMany()
06.
{
return
Counter::howMany();}
07.
private
:
08.
.....
09.
Counter c;
10.
};
11.
12.
//or:
13.
14.
// inherit from Counter to count objects
15.
class
Widget:
public
Counter {
16.
.....
17.
};
這樣,其他類就可以使用Counter計數(shù)自己的實例了,它們將互不影響。
上面兩種方案都可正確實現(xiàn)計數(shù),我們繼續(xù)探討這兩種方案的優(yōu)缺點。
首先講public繼承,即class Widget: public Counter這種方案:有經(jīng)驗的讀者肯定會想到基類Counter的析構(gòu)函數(shù)要變?yōu)樘摵瘮?shù)。否則通過基類指針delete派生類時,結(jié)果未定義(可能導(dǎo)致程序crash或其他)
?1.
Counter *pw =
new
Widget;
// get base class ptr to derived class object
2.
......
3.
delete
pw;
// yields undefined results if the base class lacks a virtual destructor
但一旦Counter有虛析構(gòu)函數(shù),就會給類帶入vTable,多占用了空間并影響客戶類的效率。解決方法可以是將析構(gòu)函數(shù)作為protected成員。這樣就不能delete pw,因為它會導(dǎo)致編譯錯誤。
?1.
template
2.
class
Counter {
3.
public
:
4.
.....
5.
protected
:
6.
~Counter() { --count; }
7.
.....
8.
};
其次,Counter作為客戶類的成員變量這種方案(這時Counter的析構(gòu)函數(shù)必須public)。一個明顯的缺點是客戶類必須定義Counter為其成員變量同時還得定義一個inline函數(shù)以調(diào)用Counter類得HowMany函數(shù)。另一個較隱蔽的缺點:它增大了客戶類所占用的內(nèi)存。Counter類沒有非靜態(tài)成員變量,有人就可能會認為Counter對象的大小為0,其實不然,C++規(guī)定所有對象的大小最小必須為1字節(jié)。所以這用方案增加了客戶類的大小。使用派生則不一樣,基類size可以0,所以public繼承方案不會增加客戶類的大小。
除了上面兩種方案,還可以使用private繼承,即class Widget: private Counter。類似于第一種方案:
?1.
class
Widget:
private
Counter {
2.
public
:
3.
// make howMany public
4.
using
Counter::howMany;
5.
6.
.....
// rest of Widget is unchanged
7.
};
它直接防止下面的代碼:
?1.
Counter *pw =
new
Widget;
//私有繼承不允許這樣轉(zhuǎn)換
綜合看來,public繼承方案已經(jīng)比較完善了。然而,還是有些值得注意的地方。假如有另一個類SpecialWidget,其繼承于Widget,對類SpecialWidget的對象計數(shù)就只能如下:
?1.
class
SpecialWidget:
public
Widget,
2.
public
Counter {
3.
public
:
4.
};
這樣,對SpecialWidget的對象計數(shù)是正確的,但對Widget對象的計數(shù)是錯誤的。這時Widget的計數(shù)是Widget類的所有對象SpecialWidget類的所有對象的總和。為什么?因為每創(chuàng)建一個SpecialWidget對象,Widget構(gòu)造函數(shù)就要調(diào)用一次,就增加一次計數(shù)。
總結(jié)
用模板實現(xiàn)的這個對象計數(shù)類可以滿足絕大多數(shù)需求,但不適用于計數(shù)有繼承關(guān)系的類。本文的核心思想來源于CUG上C++大師Scott Meyers的一篇文章并有所改動。