转载于http://www.vckbase.com/index.php/w
本文目的是实现一个实用的对C++类计数的类,同时在实现过程中指出一些容易为人忽视的C++知识。 要实现一个类的对象(实例)计数,即程序运行中此类有多少个对象存在,最容易的实现方法是使用静态数据成员。如下: 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;
注意构造函数也要增加计数,这一点很多人容易忘记。 但是如果程序中有多个需要实例计数的类,则在每个类中加入上面代码未免繁琐、易错。这种情况下,最好是实现一个通用计数类。它应该具备一下特点: 易于使用:任何需要计数的类(以下简称客户类)只要添加少数代码即可使用; 有效率:不增加客户类大小,对客户类性能没有影响; 健壮:客户类使用时,不容易误用。 下面我们将逐步实现并完善这个通用的计数类。 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类能否正确完成计数呢?例如:Widget类利用它来进行实例计数: 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完成了任务。然而,如果我们在同一进程中还需要利用Counter来计数Fish类,显然,Counter就不能胜任,因为它只有一个静态成员变量,它会将Widget和Fish的个数一起统计。这个方案不行,怎么办?用模板!如下: 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;
则上面的实现变成: 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计数自己的实例了,它们将互不影响。 上面两种方案都可正确实现计数,我们继续探讨这两种方案的优缺点。 首先讲public继承,即class Widget: public Counter这种方案:有经验的读者肯定会想到基类Counter的析构函数要变为虚函数。否则通过基类指针delete派生类时,结果未定义(可能导致程序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有虚析构函数,就会给类带入vTable,多占用了空间并影响客户类的效率。解决方法可以是将析构函数作为protected成员。这样就不能delete pw,因为它会导致编译错误。 1.
template
2.
class Counter {
3.
public :
4.
.....
5.
protected :
6.
~Counter() { --count; }
7.
.....
8.
};
其次,Counter作为客户类的成员变量这种方案(这时Counter的析构函数必须public)。一个明显的缺点是客户类必须定义Counter为其成员变量同时还得定义一个inline函数以调用Counter类得HowMany函数。另一个较隐蔽的缺点:它增大了客户类所占用的内存。Counter类没有非静态成员变量,有人就可能会认为Counter对象的大小为0,其实不然,C++规定所有对象的大小最小必须为1字节。所以这用方案增加了客户类的大小。使用派生则不一样,基类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; //私有继承不允许这样转换
综合看来,public继承方案已经比较完善了。然而,还是有些值得注意的地方。假如有另一个类SpecialWidget,其继承于Widget,对类SpecialWidget的对象计数就只能如下: 1.
class SpecialWidget: public Widget,
2.
public Counter {
3.
public :
4.
};
这样,对SpecialWidget的对象计数是正确的,但对Widget对象的计数是错误的。这时Widget的计数是Widget类的所有对象SpecialWidget类的所有对象的总和。为什么?因为每创建一个SpecialWidget对象,Widget构造函数就要调用一次,就增加一次计数。 总结 用模板实现的这个对象计数类可以满足绝大多数需求,但不适用于计数有继承关系的类。本文的核心思想来源于CUG上C++大师Scott Meyers的一篇文章并有所改动。 |
C++对象计数
最新推荐文章于 2024-03-31 11:22:17 发布