1. 针对某些有特殊功能的类,有时需要限制其对象数量,例如系统中只有一个打印机,因此需要将打印机的对象数目限制为1,或者只有16个file descriptor(文件描述器)可用,因此必须确定不会有更多的descriptor objects被产生出来,在这些情况下.就需要限制对象数目,或者说阻止对象被产生出来.
2. 允许零个或一个对象
要限制对象数目,最直接的策略是限制构造函数的调用(以下称策略1),采用这种思想将构造函数设为private,然后声明一个友元函数调用它,并生成一个static对象,像这样:
class PrintJob; class Printer { public: void submitJob(const PrintJob& job); void reset(); void performSelfTest(); ... friend Printer& thePrinter(); private: Printer(); Printer(const Printer& rhs); ... }; Printer& thePrinter(){ static Printer p; return p; }
当要使用Printer对象时,就调用thePrinter,它返回Printer的引用且保证只产生一个Printer对象,除了将thePrinter声明为friend,还可以使它成为Printer类的static成员,像这样:
class Printer { public: static Printer& thePrinter(); ... private: Printer(); Printer(const Printer& rhs); ... }; Printer& Printer::thePrinter(){ static Printer p; return p; }
thePrinter的实现使用了函数的static对象,与class的static对象相比,函数中的static对象保证在第一次调用该函数时才被初始化,这可以避免由于"C++没有规定定义于不同编译单元内的全局对象的初始化顺序"而导致的错误.
另外一种策略(以下称策略2)是当外界申请太多对象时,在构造函数内抛出异常:
class Printer { public: class TooManyObjects{}; Printer(); ~Printer(); ... private: static size_t numObjects; Printer(const Printer& rhs);//由于只允许产生一个对象,所以不允许拷贝 }; size_t Printer::numObjects = 0; Printer::Printer(){ if (numObjects >= 1) { throw TooManyObjects(); } proceed with normal construction here; ++numObjects; } Printer::~Printer(){ perform normal destruction here; --numObjects; }
策略2很容易被一般化,而且可以使对象数目限制在除1以外的值,但在涉及到类的继承以及内含的情况下不起作用,因为"Printer对象可于三种不同状态下生存:(1)它自己,(2)派生物的‘base class‘成分,(3) 内嵌于较大对象之中",例如:
涉及继承:
//彩色打印机类 class ColorPrinter: public Printer { ... }; Printer p; ColorPrinter cp; //抛出异常
涉及内含:
class CPFMachine { private: Printer p; FaxMachine f; CopyMachine c; ... }; CPFMachine m1; CPFMachine m2; //抛出异常
策略1不存在这个问题,因为将构造函数设为private实际上禁止了继承和内含的发生.由此启发,要禁止类被继承,可以将构造函数设为private,然后通过开放的接口提供对象引用或指针.
将两种策略结合,使其既有策略1的禁止被继承和内含的特点,又具有策略2的一般化特性,像这样:
class Printer { public: class TooManyObjects{}; static Printer * makePrinter(); static Printer * makePrinter(const Printer& rhs); ... private: static size_t numObjects; static const size_t maxObjects = 10; // 对象的数目限制,也可以使用枚举 Printer(); Printer(const Printer& rhs); }; size_t Printer::numObjects = 0; const size_t Printer::maxObjects; Printer::Printer(){ if (numObjects >= maxObjects) { throw TooManyObjects(); } ... } Printer::Printer(const Printer& rhs){ if (numObjects >= maxObjects) { throw TooManyObjects(); } ... } Printer * Printer::makePrinter(){ return new Printer; } Printer * Printer::makePrinter(const Printer& rhs){ return new Printer(rhs); }
3. 一个用来计算对象个数的Base Classes
2提出的策略1和策略2的结合可以一般化,将它抽象为一个类模板,任何需要限制对象数目的类只要继承这个模板的实例化即可:
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; }
将Counted定义为模板,同一继承层次中的不同类共享同一对象计数,因此通过使用类模板,不同派生类的对象计数得以相互独立.
Printer要使用Counted就像这样:
class Printer: private Counted<Printer> { public: static Printer * makePrinter(); static Printer * makePrinter(const Printer& rhs); ~Printer(); void submitJob(const PrintJob& job); void reset(); void performSelfTest(); ... using Counted<Printer>::objectCount; using Counted<Printer>::TooManyObjects; private: Printer(); Printer(const Printer& rhs); };
由于Counted<Printer>是Printer的基类,因此构造Printer之前一定会发生Counted<Printer>的构造,这样就把限制对象个数的任务交由基类Counted<Printer>来完成.此外,由于Printer只继承Counted的实现而不继承接口,因此使用private继承.如果要使开放Counted的部分接口,可以使用using declaration:
class Printer: private Counted<Printer> { public: ... using Counted<Printer>::objectCount; ... };
或旧式的访问声明语法:
class Printer: private Counted<Printer> { public: ... Counted<Printer>::objectCount; ... };
最后一个需要注意的地方是,static成员的定义问题,由于Counted类模板使用了static成员,因此必须要在类外定义,对于numObjects可以在头文件中初始化为0,但用于限制对象个数的maxObjects只能由用户定义并初始化,如果用户忘记定义,链接时将会报错并提醒.
More Effective C++ 条款26 限制某个class所能产生的对象数量
原文:http://www.cnblogs.com/reasno/p/4841418.html