限制类对象生成的数量(二)

    技术2022-05-11  74

     限制类对象生成的数量,如果我们需要的数值不是“1”,而是某个其它的某个值怎么办?最容易想到的办法是通过添加静态控制变量--计数器来达到目的。假设我们需要为打印机设计一个类,首先可能会这样设计:

    class  Printer{ public :     class  TooManyObjects{};    //  当需要的对象过多时就使用这个异常类       Printer();    Printer( const  Printer &  rhs);     ~ Printer();    ... private :     static  size_t numObjects; }; // 定义静态类成员 size_t Printer::numObjects  =   0 ; Printer::Printer(){     if  (numObjects  >=   1 ){         throw  TooManyObjects();    }      // 继续运行正常的构造函数;     ...     ++ numObjects;}Printer::Printer( const  Printer &  rhs){     if  (numObjects  >=   1 ){         throw  TooManyObjects();    }      // 继续运行正常的构造函数;     ...     ++ numObjects;}Printer:: ~ Printer(){     // 进行正常的析构函数处理;     ...     -- numObjects;}

     

    核心思想就是使用numObjects跟踪Pritner对象存在的数量。当构造类时,它的值就增加,释放类时,它的值就减少。如果试图构造过多的Printer对象,就会抛出一个TooManyObjects类型的异常。这种方法可以很直观地把限制数量推广大到大于1的任何值。

    但是,注意到这里的构造函数是公有的。这种方法可能会带来你意想不到的结果。假设我们一个特殊的打印机,是彩色打印机。这种打印机类有许多地方与普通的打印机类相同,所以我们从普通打印类继承下来:

    class  ColorPrinter:  public  Printer{    ... }; 现在假设我们系统有一个普通打印机和一个彩色打印机: Printer p;ColorPrinter cp;

    这两个定义会产生多少Pritner对象?答案是两个:一个是p,一个是cp。在运行时,当构造cp的基类部分时,会抛出TooManyObjects异常。对于许多程序员来说,这可不是他们所期望的事情。当其它对象包含Printer对象时,会发生同样的问题:

    class  FaxMachine; class  CopyMachine; class  CPFMachine{                            //  一种机器,可以复印,打印,发传真。 private :                                         Printer p;                                      //  有打印能力     FaxMachine f;                               //  有传真能力     CopyMachine c;                            //  有复印能力     ...};CPFMachine m1;                               //  运行正常 CPFMachine m2;                               //  抛出 TooManyObjects异常

    问题是Printer对象能存在于三种不同的环境中:只有它们本身;作为其它派生类的基类;被嵌入在更大的对象里。存在这些不同环境,极大地混淆了跟踪“存在对象的数目” 的含义,因为你心目中的“对象的存在” 的含义与编译器不一致。因此,我们还是必须将构造函数放在private域内,并设计相应的伪构造函数。另外限制数值用静态常量代替。

    class  Printer{ public :     class  TooManyObjects{};     ~ Printer();     //  伪构造函数      static  Printer  *  makePrinter();     static  Printer  *  makePrinter( const  Printer &  rhs);     ...  private :     static  size_t numObjects;     static   const  size_t maxObjects;          Printer();    Printer( const  Printer &  rhs);}; // 定义静态类成员 size_t Printer::numObjects  =   0 ; const  size_t Printer::maxObjects  =   10 ;Printer::Printer(){     if  (numObjects  >=  maxObjects){         throw  TooManyObjects();    }    ...      ++ numObjects;}Printer::Printer( const  Printer &  rhs){     if  (numObjects  >=  maxObjects){         throw  TooManyObjects();    }    ...     ++ numObjects;}Printer:: ~ Printer(){        ...     -- numObjects;}Printer  *  Printer::makePrinter(){  return   new  Printer; }Printer  *  Printer::makePrinter( const  Printer &  rhs){  return   new  Printer(rhs); } 

    至此,一个最多允许10个Printer对象存在的类定义好了。但是,如果我们有大量像Printer需要限制实例数量的类,就必须一遍又一遍地编写一样的代码,每个类编写一次。因此我们考虑把静态成员计数器(numObjects)和限制值(maxObjects)封装在一个类里,编写一个具有实例计数功能的基类,然后让像Printer这样的类从该基类继承。

    通常的基类能达到这个目的吗?别忘了静态成员是隶属于类本身的。也就是说如果类A和类B都从这个基类继承,他们将共用基类的静态成员。这样岂不是会产生混乱,完全不能达到分别计数的目的。而且别忘了限制值(maxObjects)是一个静态常量,只能被一次赋值一次。因此我们需要确保每个进行实例计数的类都有一个相互隔离的计数器。使用计数类模板可以自动生成适当数量的计数器,能让计数器成为从模板中生成的类的静态成员。(给类模板赋予不同的模板参数值,将生成不同的实体类)

    // Counted.h template < class  BeingCounted > class  Counted{ public :     class  TooManyObjects{};                      //  用来抛出异常      static   int  objectCount(){  return  numObjects; } protected :    Counted();    Counted( const  Counted &  rhs);     ~ Counted(){  -- numObjects; } private :     static   int  numObjects;     static   const  size_t maxObjects;     void  init();                                 //  避免构造函数的 };                                                //  代码重复 template < class  BeingCounted > Counted < BeingCounted > ::Counted(){ init(); }template < class  BeingCounted > Counted < BeingCounted > ::Counted( const  Counted < BeingCounted >& ){ init(); }template < class  BeingCounted > void  Counted < BeingCounted > ::init(){     if  (numObjects  >=  maxObjects)  throw  TooManyObjects();     ++ numObjects;} // Printer.h class  Printer:  private  Counted < Printer > { public :     //  伪构造函数      static  Printer  *  makePrinter();         ~ Printer();     void  submitJob();         using  Counted < Printer > ::objectCount;      //  让objectCount在Printer中是public     // Counted<Printer>::objectCount;          //  作用同上      using  Counted < Printer > ::TooManyObjects;   private :    Printer();    Printer( const  Printer &  rhs);              // 声明但不定义,禁止拷贝构造 }; // Printer.cpp #include  " Printer.h " // 定义静态成员 int  Counted < Printer > ::numObjects;  const  size_t Counted < Printer > ::maxObjects  =   10 ;Printer  *  Printer::makePrinter(){     return   new  Printer();}Printer::Printer(){       ...}Printer:: ~ Printer(){    ...} void  Printer::submitJob(){    cout  <<   " Submit job "   <<  endl;}

     

    当Printer继承Counted<Printer>时,它可以忘记有关对象计数的事情。编写Printer类时根本不用考虑对象计数,就好像有其他人会为它计数一样。这里有趣的不是你所见到的东西,而是你看不到的东西。不检测对象的数量就好像限制将被超过,执行完构造函数后也不增加存在对象的数目。所有这些现在都由Counted<Printer>的构造函数来处理,因为Counted<Printer>是Printer的基类,我们知道Counted<Printer>的构造函数总在Printer的前面被调用。如果建立过多的对象,Counted<Printer>的构造函数就会抛出异常,甚至都没有调用Printer的构造函数。

     


    最新回复(0)