1 template<class T> 2 class Counted 3 { 4 public: 5 class TooManyObjects{} 6 static int objectCount(){ return numObjects; } 7 protected: 8 Counted(); 9 Counted(const Counted&); 10 ~Counted() { --numObjects; } 11 private: 12 static int numObjects = 0; 13 const int maxObjects; 14 void init(); 15 } 16 17 template<class T> 18 Counted<T>::Counted() 19 { 20 init(); 21 } 22 23 template<class T> 24 Counted<T>::Counted(const Count&) 25 { 26 init(); 27 } 28 29 template<class T> 30 void Counted<T>::init() 31 { 32 if (numObjects >= maxObjects) 33 throw TooManyObjects; 34 35 ++numObjects; 36 } 37 38 --------------------------------------------------------------------- 39 const int maxObjects = 10; 40 class Printer : private Counted<Printer> 41 { 42 public: 43 Printer(); 44 Printer(const Printer&); 45 46 ~Printer(); 47 48 using Counted<Printer>::objectCount; 49 using Counted<Printer>::TooManyObjects; 50 51 private: 52 53 }
我们可以很明显的看出上面代码中,Counted这个基类完成了对对象数量的控制。
Printer使用了Counter模板来跟踪存在多少Printer对象,坦率地说,除了Printer的编写者,没有人关心这个事实。它的实现细节最好是private,这就是为什么这里使用private继承的原因(参见Effective C++条款42)。另一种方法是在Printer和counted<Printer>之间使用public继承,但是我们必须给Counted类一个虚拟析构函数。(否则如果有人通过Counted<Printer>*指针删除一个Printer对象,我们就有导致对象行为不正确的风险——参见Effective C++条款14。)条款24已经说得很明白了,在Counted中存在虚函数,几乎肯定影响从Counted继承下来的对象的大小和布局。我们不想引入这些额外的负担,所以使用private继承来避免这些负担。
对于13行中 maxObjects 没有进行初始化赋值,是因为我们不能确定最大数量是多少,因此将这个事情交给客户端去处理,可以在使用这个基类的时候在声明文件里对它进行赋值。
对于基类使用模版这个问题,如果不使用模版,对于多个类都需要进行限制数量的时候我们需要多次编写相同内容不同名称的基类,非常繁琐。如果让它们共通继承一个基类,则会产生限制的最大数是这个几个类产生对象的总数的问题。使用模版,则可以保证,对于不同的类,编译器都会产生不同类型的基类,单独的对应各个需要限制的类。
49行这个的目的是使得 TooManyObjects 这个异常类可以在对象外部调用,从而让客户端能够实现异常处理。