【C++ techniques】限制某个class所能产生的对象数量

允许零个或一个对象

  • 每当即将产生一个对象,就会有一个constructor被调用;
  • “阻止某个class产出对象”的最简单方法:
    将其constructorsh声明为private
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对象:

  • C++的一个哲学基础是,你不应该为你不适用的东西付出任何代价
  • “class拥有一个static对象”的意思:
    即使从未被用到,它也会被构造(及析构);
  • ”函数拥有一个static对象“的意思:
    此对象在函数第一次被调用时才产生,如果该函数从未被调用,这个对象也就绝不会诞生(然而你必须付出代价,在函数每次被调用时检查对象是否需要诞生)。
  • C++对于“同一编译单元内的statics”初始化顺序是有提出一些保证的,但对于“不同编译单元内的statics”的初始化顺序没有任何说明;
  • 一个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

我们可以完成一个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);
};

你可能感兴趣的:(C++进阶,c++,开发语言,笔记)