class CantBeInstantiated
{
private:
CantBeInstantiated();
CantBeInstantiated(const CantBeInstantiated&);
...
};
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;
}
以上设计有3个成分:
1. Printer类的构造函数为private,这样可以压制对象的产生;
2. 全局函数thePrinter被声明为此类的一个友元函数,致使thePrinter不受私有构造函数的约束;
3. thePrinter内含一个静态的Printer对象,意思是只有一个Printer对象会被产生出来。
//一旦client需要和系统中唯一那台打印机打交道,就调用thePrinter
//返回一个reference,代表一个Printer对象
//所以thePrinter可以用在任何需要Printer对象的地方:
class PrintJob
{
public:
PrintJob(const string& whatToPrint);
...
};
string buffer;
... //把数据放进buffer内
thePrinter.reset();
thePrinter.submitJob(buffer);
//将thePrinter设置成Printer的一个static member function之后:
class Printer
{
public:
static Printer& thePrinter();
...
private:
Printer();
Printer(const Printer& rhs);
...
};
PrintJob& Printer::thePrinter()
{
static Printer p;
return p;
}
Printer::thePrinter().reset();
Printer::thePrinter().submitJob(buffer);
//或者
using PrintingStuff::thePrinter;
thePrinter().reset();
thePrinter().submitJob(buffer);
在此thePrinter的实现代码中,有两个精细的地方值得探讨:
1. 形成唯一一个Printer对象是函数中static对象而非class中static对象:
- “class拥有一个static对象”的意思:
即使从未被用到,它也会被构造(及析构);- ”函数拥有一个static对象“的意思:
此对象在函数第一次被调用时才产生,如果该函数从未被调用,这个对象也就绝不会诞生(然而你必须付出代价,在函数每次被调用时检查对象是否需要诞生)。
- 一个function static的初始化时机:在函数第一次被调用,并且在该static被定义处;
- 一个class static(或者global static)则不一定在什么时候初始化。
2. 函数的“static对象与inline的互动”:
- inline在概念上意味着编译器应该将每一个调用以函数本身取代;
- non-member functions意味着这个函数有内部链接;程序的目标代码可能会对带有内部链接的函数复制一份以上的代码,也包括复制函数的static对象。
现在的想法是,利用numObjects来追踪目前存在多少个Printer对象。这个数值将在constructor中累加,并在destructor中递减。如果外界企图构造太多的Printer对象,我们就抛出一个类型为TooManyObjects的exception:
size_t Printer::numObjects = 0;
Printer::Printer()
{
if(numObjects >= 1)
throw TooManyObjects();
proceed with normal construction here;
++numObjects;
}
Printer::~Printer()
{
perform normal desturction here;
--numObjects;
}
//设计一台彩色打印机,和一般打印机有许多共同点,所以使用继承机制
class ColorPrinter:public Printer
{
...
};
Printer p;
ColorPrinter cp;
class CPFMachine //针对那些可影印、可打印、可传真的机器
{
private:
Printer p; //针对打印
FaxMachine f; //针对传真
CopyMachine c; //针对影印
...
};
CPFMachine m1; //没问题
CPFMachine m2; //抛出一个TooManyObjects exception。
问题出在Printer对象可于3种不同状态下生存:
1. 它自己;
2. 派生物的“base class 成分”;
3. 内嵌于较大对象之中。
这些不同状态的呈现把”追踪目前存在的对象个数“的意义严重弄混了。
class FSA
{
public:
//pseudo(伪)constructor
static FSA* makeFSA();
static FSA* makeFSA(const FSA& rhs);
...
private:
FSA();
FSA(const FSA& rhs);
...
};
FSA* FSA::makeFSA()
{
return new FSA();
}
FSA* FSA::makeFSA(const FSA& rhs)
{
return new FSA(rhs);
}
不像thePrinter函数总是返回一个reference代表唯一对象,这里的每一个makeFSA pseudo-constructor都返回一个指针,指向独一无二的对象。即允许产生无限个数的FSA对象。
但是每一个pseudo~constructor都调用了new,意味调用者必须记得调用delete,否则就会出现资源泄漏的问题。如果希望delete自动被调用,也可以使用auto_ptr,见【C++ Exceptions】利用destructors避免泄露资源:
//间接调用default FSA constructor
auto_ptr<FSA> pfsal(FSA::makeFSA());
//间接调用FSA copy constructor
auto_ptr<FSA> pfsa2()FSA::makeFSA(*pfsa1);
将对象计数(object-counting)码和伪构造函数(pseudo-constructor)结合起来:
class Printer
{
public:
class TooManyObjects{};
//pseudo-constructor
static Printer* makePrinter();
~Printer();
void submitJob(const PrintJob& job);
void reset();
void performSelfTest();
...
private:
static size_t numObjects;
Printer();
Printer(const Printer& rhs); //不要定义此函数,我们决不允许复制行为
};
size_t Printer::numObjects = 0;
Printer::Printer()
{
if(numObjects >= 1)
throw TooManyObjects();
proceed with normal object construction here;
++numObjects;
}
Printer* Printer::makePrinter()
{
return new Printer;
}
//Clients使用这个Printer class,就像使用任何其他class一样
//只不过他们必须调用pseudo-constructor,取代真正的constructor:
Printer p; //错误!default ctor是private
Printer* p2 = Printer::makePrinter(); //没问题,间接调用default ctor
Printer* p3 = *p2; //错误!copy ctor是private
p2->performSelfTest(); //所有其他函数都像平常一般被调用之
p2->reset();
...
delete p2;
这项技术很容易被泛化为“任意个数(不止一个)的对象”。我们唯一要做的就是将原来写死的常量1改为class专属的一个数值,然后将复制对象的限制去掉。例如:
//下面是修订后的Printer class,允许最多10个Printer对象同时存在
class Printer
{
public:
class TooManyObjects{};
//pseudo-constructor
static Printer* makePrinter();
static Printer* makePrinter(const Printer& rhs);
~Printer();
void submitJob(const PrintJob& job);
void reset();
void performSelfTest();
...
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);
}
我们可以完成一个base class,作为对象计数之用,并让诸如Printer之类的classes继承它。
//令计数器为“template所产生的classes”内的一个static member:
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(); //用来避免ctor代码重复出现
};
template<class BeingCounted>
Counted<BeingCounted>::Counted()
{
init();
}
template<class BeingCounted>
Counted<BeingCounted>::Counted(const Counted<BeingCounted>&)
{
init();
}
template<class BeingCounted>
Counted<BeingCounted>::init()
{
if(numObjects >= maxObjects)
throw TooManyObjects();
++numObjects;
}
//修改Pirnter class,让它运用Counted template:
class Printer:private Counted<Printer>
{
public:
//pseudo-constructors
static Printer* makePrinter);
static Printer* makePrinter(const Printer& rhs);
~Printer();
using Counted<Printer>::objectCount; //让Printer用户访问objectCount
void submitJob(const PrintJob& job);
void reset();
void performSelfTest();
...
using Counted<Printer>::objectCount;
using Counted<Printer>::TooManyObjects;
private:
Printer();
Printer(const Printer& rhs);
};