MoreEffectiveC++Item35 条款26: 限制某个class所能产生的对象个数
一 允许零个或一个对象
我们知道每当即将产生一个对象,我们有一个constructor被调用,那么我们现在想组织某个对象的产生,最简单的方法就是将其构造函数声明成private(这样做同事防止了这个类被继承)
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;
}
看到这里我们可能会想到,创建一个类,然后类中声明一个static Printer对象,用一个函数返回它也可以呀,答案是不行
缺点1:class中包含一个static对象,和函数中拥有一个static对象是由差别的,calass中拥有一个static对象的意思是,无论你是否用到过这个对象,它都会被构造(及析构).
相反函数中拥有一个static对象,只有在第一次调用此函数时,这个对象才会产生,如果函数从未被调用,这个对象就不会诞生(然而你必须付出,每次调用函数时都会检查对象是否要诞生)
缺点2:调用的时机,class static的初始化时机我们无法控制,而function static可以.
下面我们来介绍一种控制类创建个数的方法
class Printer {
public:
class TooManyObjects{}; //当外界申请太多对象时抛出这种exception class
Printer();
~Printer();
...
private:
static size_t numObjects;
Printer(const Printer& rhs);//由于只允许产生一个对象,所以不允许拷贝
};
size_t Printer::numObjects = ;// static做成员变量 必须在class外初始化
Printer::Printer(){
if (numObjects >= ) {
throw TooManyObjects();
}
proceed with normal construction here;
++numObjects;
}
Printer::~Printer(){
perform normal destruction here;
--numObjects;
}
二 不同对象的构造状态
现在我们创建一个带彩印的打印机,这种打印机类有许多地方与普通的打印机类相同,所以我们从普通打印类继承下来
class ColorPrinter: public Printer {
...
};
//这时我们创建一个打印机和一个彩印机
Printer p;
ColorPrinter cp;
这两个定义会产生多少 Pritner 对象?答案是两个:一个是 p,一个是 cp。在运行时,当构造 cp 的基类部分时,会抛出 TooManyObjects 异常
如果class CPFMachine 中含有class Printer 那么我们创建两个class CPFMachine 时也会抛出 TooManyObjects 异常
class CPFMachine { // 一种机器,可以复印,打印
private: // 发传真。
Printer p; // 有打印能力
FaxMachine f; // 有传真能力
CopyMachine c; // 有复印能力
...
};
CPFMachine m1; // 运行正常
CPFMachine m2; // 抛出 TooManyObjects 异常
问题出在Printer对象可与3中不同状态下存在
1.它自己
2.派生类的base class成分
3.内嵌于较大的对象之中
现在假设你想创建一个可以产生任意数量的类,且你不希望任何类继承自它
class FSA {
public:
// 伪构造函数
static FSA * makeFSA();
static FSA * makeFSA(const FSA& rhs);
...
private:
FSA();
FSA(const FSA& rhs);
...
auto_ptr<FSA> pfsa1(FSA::makeFSA());//因为调用new,所以我们必须得delete,使用auto_ptr就不用考虑delete了
auto_ptr<FSA> pfsa2(FSA::makeFSA(*pfsa1));
...
};
三 一个用来计数对象的Base Class
可以将上面的方法结合一般化,将他抽象一个类模板,任何需要限制对象数目的类只要继承自这个类模板即可
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定义为模板,同一继承层次中的不同类共享同一对象计数,因此通过使用类模板,不同派生类的对象计数得以相互独立
从这个模板生成的类仅仅能被做为基类使用,因此构造函数和析构函数被声明为protected。注意 private 成员函数 init 用来避免两个 Counted 构造函数的语句重复
现在我们更改Printer class让她运用Counted template
class Printer: private Counted<Printer> {//注意我们这里采用私有继承,可以不必声明虚析构函数.那么这个时候我们使用Counted<Printer>删除一一个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; //让此函数对于printer的用户而言成为public
using Counted<Printer>::TooManyObjects;
private:
Printer();
Printer(const Printer& rhs);
};
现在所 有 这 些 现 在 都 由Counted<Printer>的构造函数来处理,因为 Counted<Printer>是 Printer 的基类,我们知道 Counted<Printer>的构造函数总在 Printer 的前面被调用。如果建立过多的对象,Counted<Printer>的构造函数就会抛出异常,甚至都没有调用 Printer 的构造函数.
如果想要限制生成对象的个数则需在
//增加类模板实现文件
template<class BeingCounted> // 定义 numObjects
int Counted<BeingCounted>::numObjects; // 自动把它初始化为 0
如果Printer想要使用加限制生成对象数量的类模板则需在Printer加这个实现
const size_t Counted<Printer>::maxObjects = ;
MoreEffectiveC++Item35 条款26: 限制某个class所能产生的对象个数的更多相关文章
- MoreEffectiveC++Item35 条款27: 要求或禁止对象产生于heap中
一 要求对象产生在heap中 阻止对象产生产生在non-heap中最简单的方法是将其构造或析构函数声明在private下,用一个public的函数去调用起构造和析构函数 class UPNumber ...
- MoreEffectiveC++Item35 条款25 将constructor和non-member functions虚化
1.virtual constructor 在语法上是不可将构造函数声明成虚函数,虚函数用于实现"因类型而异的行为",也就是根据指针或引用所绑定对象的动态类型而调用不同实体.现在所 ...
- MoreEffectiveC++Item35(异常)(条款9-15)
条款9 使用析构函数防止内存泄漏 条款10 在构造函数中防止内存泄漏 条款11 禁止异常信息传递到析构函数外 条款12 理解"抛出一个异常''与"传递一个参数"或调用一个 ...
- EC读书笔记系列之14:条款26、27、28、29、30、31
条款26 尽可能延后变量定义式的出现时间(Lazy evaluation) 记住: ★尽可能延后变量定义式的出现.这样做可增加程序的清晰度并改善程序效率 ----------------------- ...
- MoreEffectiveC++Item35(效率)(条款16-24)
条款16 谨记80-20法则 条款17 考虑使用 lazy evaluation(缓释评估) 条款18 分期摊还预期的计算成本 条款19 了解临时对象的来源 条款20 协助完成"返回值的优化 ...
- MoreEffectiveC++Item35(操作符)(条款5-8)
条款5 对定制的"类型转换函数"保持警惕 条款6 区别increment/decrement操作符的前值和后置形式 条款7 千万不要重载&&,||,和,操作符 条款 ...
- MoreEffectiveC++Item35(基础议题)(条款1-4)
条款1:区别指针和引用 条款2:最好使用C++转换操作符 条款3: 绝对不要以多态的方式处理数组 条款4: 避免无用的缺省构造函数 条款1:区别指针和引用 1.指针(pointer) 使用[*/-&g ...
- 《Effective C++》条款26 防卫潜伏的ambiguity模棱两可的状态
每个人都有思想.有些人相信自由经济学,有些人相信来生.有些人甚至相信COBOL是一种真正的程序设计语言.C++也有一种思想:它认为潜在的二义性不是一种错误.ambiguity 这是潜在二义性的一个例子 ...
- Effective C++ -----条款26:尽可能延后变量定义式的出现时间
尽可能延后变量定义式的出现.这样做可增加程序的清晰度并改善程序效率.
随机推荐
- Ubuntu server 安装的mysql数据库忘记密码的解决方法
客户端连接时报错MySQL数据库出现:Error 1045错误时,就表明输入的用户名或密码错误被拒绝访问了. 解决办法可以分为以下几步: 1.修改mysql配置文件,使得可以无密码登录mysql su ...
- HDU2189 来生一起走
好久没发博客了,最近遇到以下奇葩错误,不明觉厉,忍不住发一篇 /*母函数,因为要求的是素数,那么先打一个素数表,所有的因子都是素数构成 但是遇到一个奇葩事,当num初值取1,结果就出不来,运行了好久 ...
- Django学习笔记之django-debug-toolbar使用指南
介绍 django-debug-toolbar 是一组可配置的面板,可显示有关当前请求/响应的各种调试信息,并在单击时显示有关面板内容的更多详细信息. github地址 文档地址 安装 pip3 in ...
- 20145109 《Java程序设计》第九周学习总结
JDBC 1 . DriverManager Class.forName("oracle.jdbc.driver.OracleDriver").newInstance(); 2 . ...
- spring boot加mybatis使用Map返回时,当值为空时属性也会没有(转)
使用spring boot加mybatis时,设置Map返回,当值为空时属性也会没有,就会报错 在application.properties中加入下面配置,将会解决这个问题. #当查询数据为空时 ...
- $(document).ready(function(){})和$(window).load(function(){})的区别
页面加载完成有两种状态: 1.Ready:表示文档结构已经加载完成,不包含图片等非文字媒体文件,在Dom节点创建完成后执行,如果有多个定义则依次执行 $(document).ready(functio ...
- DVWA安装
DVWA安装: 启动xampp下的apache中间件和mysql 将dvwa放到xampp下的htdocs目录下 在浏览器输入http://127.0.0.1/dvwa 即可使用啦! 还有owasp的 ...
- html-w3c规范及常见标签
W3C提倡的web结构: 内容(HTML)与表现(css样式)分离 内容(HTML)与行为(JS)分离 HTML内容结构要求语义化 基本规范: 标签名和属性名称必须小写 HTML标签必须关闭 属性值必 ...
- 跟着vamei复习概率论
最近重新看了一下概率论,感觉很多东西都遗忘了,还会陷入各种误区,赶紧的纠正回来. 概率论这块,主要内容包括: 事件.条件概率.随机变量.随机变量的分布函数.概率密度.联合分布.期望.方差.协方差. 我 ...
- 我的nlp之路(1)
1/8日任务 基础篇: 如何使用远程连接从windows或者linux连到服务器进行操作(切换用户,传递文件) (严禁使用root账户) Linux基本bash命令 a) 查看文件大小, ...