More Effective C++ 条款26(下)

Effective C++ 第三版 所有条款总结及对应代码描述(纯干货) Effective C++ 第三版 所有条款总结及对应代码描述(纯干货) 阅读详情

条款26:限制某个类所能产生的对象数量(下) 

允许对象来去自由

我们知道如何设计只允许建立一个实例的类,我们知道跟踪特定类的对象数量的工作是复杂的,因为在三种不同的环境中都可能调用对象的构造函数,我们知道消除对象计数中混乱现象的方法是把构造函数声明为private。还有最后一点值得我们注意:使用thePrinter函数封装对单个对象的访问,以便把Printer对象的数量限制为一个,这样做的同时也会让我们在每一次运行程序时只能使用一个Printer对象。导致我们不能这样编写代码:

建立 Printer 对象 p1;

 

使用 p1;

 

释放 p1;

 

建立Printer对象p2;

 

使用 p2;

 

释放 p2;

 

...

这种设计在同一时间里没有实例化多个Printer对象,而是在程序的不同部分使用了不同的Printer对象。不允许这样编写有些不合理。毕竟我们没有违反只能存在一个printer的约束。就没有办法使它合法化么?

当然有。我们必须把先前使用的对象计数的代码与刚才看到的伪构造函数代码合并在一起:

class Printer {

public:

  class TooManyObjects{};

 

  // 伪构造函数

  static Printer * makePrinter();

 

~Printer();

 

  void submitJob(const PrintJob& job);

  void reset();

  void performSelfTest();

  ...

 

private:

  static size_t numObjects;

 

  Printer();

 

  Printer(const Printer& rhs);        //我们不定义这个函数

};                                    //因为不允许

                                       //进行拷贝

                                     // (参见Effective C++条款27)

 

// Obligatory definition of class static

size_t Printer::numObjects = 0;

 

Printer::Printer()

{

  if (numObjects >= 1) {

    throw TooManyObjects();

  }

 

  继续运行正常的构造函数;

 

  ++numObjects;

}

 

Printer * Printer::makePrinter()

{ return new Printer; }

当需要的对象过多时,会抛出异常,如果你认为这种方式给你的感觉是unreasonably harsh,你可以让伪构造函数返回一个空指针。当然客户端在使用之前应该进行检测。

除了客户端必须调用伪构造函数,而不是真正的构造函数之外,它们使用Printer类就象使用其他类一样:

Printer p1;                               // 错误! 缺省构造函数是

                                          // private

 

Printer *p2 =

  Printer::makePrinter();                 // 正确, 间接调用

                                          // 缺省构造函数

 

Printer p3 = *p2;                          // 错误! 拷贝构造函数是

                                          // private

 

p2->performSelfTest();                   // 所有其它的函数都可以

p2->reset();                             // 正常调用

 

...

 

delete p2;                                // 避免内存泄漏,如果

                                          // p2 是一个 auto_ptr,

                                          // 就不需要这步。

这种技术很容易推广到限制对象为任何数量上。我们只需把hard-wired常量值1改为根据某个类而确定的数量,然后消除拷贝对象的约束。例如,下面这个经过修改的Printer类的代码实现,最多允许10个Printer对象存在:

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);

};

 

// Obligatory definitions of class statics

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); }

如果你的编译器不能编译上述类中Printer::maxObjects的声明,这丝毫也不奇怪。特别是应该做好准备,编译器不能编译把10做为初值赋给这个变量这条语句。给static const成员(例如int, char, enum等等)确定初值的功能是最近才加入到C++中的,所以一些编译器还不允许这样编写。如果没有及时更新你的编译器,可以把maxObjects声明为在一个private内匿名枚举类型里的枚举元素,

class Printer {

private:

  enum { maxObjects = 10 };                // 在类中,

  ...                                      // maxObjects为常量10

};

 

或者象non-const static成员一样初始化static常量:

 

class Printer {

private:

  static const size_t maxObjects;            // 没有赋给初值

 

  ...

 

};

 

// 放在一个代码实现的文件中

const size_t Printer::maxObjects = 10;

后面这种方法与原来的方法有一样的效果,但是显示地确定初值能让其他程序员更容易理解。当你的编译器支持在类定义中给const static成员赋初值的功能时,你应该尽可能地利用这个功能。

一个具有对象计数功能的基类

把初始化静态成员撇在一边不说,上述的方法使用起来就像咒语一样灵验,但是另一方面它也有些繁琐。如果我们有大量像Printer需要限制实例数量的类,就必须一遍又一遍地编写一样的代码,每个类编写一次。这将会使大脑变得麻木。应该有一种方法能够自动处理这些事情。难道没有方法把实例计数的思想封装在一个类里吗?

我们很容易地能够编写一个具有实例计数功能的基类,然后让像Printer这样的类从该基类继承,而且我们能做得更好。我们使用一种方法封装全部的计数功能,不但封装维护实例计数器的函数,而且也封装实例计数器本身。(当我们在条款29中测试引用计数时,将看到需要同样的技术。有关这种设计的测试细节,参见我的文章counting objects)

Printer类的计数器是静态变量numObjects,我们应该把变量放入实例计数类中。然而也需要确保每个进行实例计数的类都有一个相互隔离的计数器。使用计数类模板可以自动生成适当数量的计数器,因为我们能让计数器成为从模板中生成的类的静态成员:

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;

}

从这个模板生成的类仅仅能被做为基类使用,因此构造函数和析构函数被声明为protected。注意private成员函数init用来避免两个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);

};

Printer使用了Counter模板来跟踪存在多少Printer对象,坦率地说,除了Printer的编写者,没有人关心这个事实。它的实现细节最好是private,这就是为什么这里使用private继承的原因(参见Effective C++条款42)。另一种方法是在Printer和counted<Printer>之间使用public继承,但是我们必须给Counted类一个虚拟析构函数。(否则如果有人通过Counted<Printer>*指针删除一个Printer对象,我们就有导致对象行为不正确的风险——参见Effective C++条款14。)条款24已经说得很明白了,在Counted中存在虚函数,几乎肯定影响从Counted继承下来的对象的大小和布局。我们不想引入这些额外的负担,所以使用private继承来避免这些负担。

Counted所做的大部分工作对于Printer的客户端来说都是隐藏的,但是这些客户端可能很想知道有当前多少Printer对象存在。Counted模板提供了objectCount函数,用来提供这种信息,但是因为我们使用private继承,这个函数在Printer类中成为了private。为了恢复该函数的public访问权,我们使用using声明:

class Printer: private Counted<Printer> {

public:

  ...

  using Counted<Printer>::objectCount; // 让这个函数对于Printer

                                       //是public

  ...   

};

这样做是合乎语法规则的,但是如果你的编译器不支持命名空间,编译器就不允许这样做。如果这样的话,你应使用老式的访问权声明语法:

class Printer: private Counted<Printer> {

public:

  ...

  Counted<Printer>::objectCount;       // 让objectCount

                                       // 在Printer中是public

  ...

 

};

这种更传统的语法与uning声明具有相同的含义。但是我们不赞成这样做。TooManyObjects类应该也应用同样的方式来处理,因为Printer的客户端如果要捕获这种异常类型,它们必须有能力访问TooManyObjects。.

当Printer继承Counted<Printer>时,它可以忘记有关对象计数的事情。编写Printer类时根本不用考虑对象计数,就好像有其他人会为它计数一样。Printer的构造函数可以是这样的:

Printer::Printer()

{

  进行正常的构造函数运行

}

这里有趣的不是你所见到的东西,而是你看不到的东西。不检测对象的数量就好像限制将被超过,执行完构造函数后也不增加存在对象的数目。所有这些现在都由Counted<Printer>的构造函数来处理,因为Counted<Printer>是Printer的基类,我们知道Counted<Printer>的构造函数总在Printer的前面被调用。如果建立过多的对象,Counted<Printer>的构造函数就会抛出异常,甚至都没有调用Printer的构造函数。

最后还有一点需要注意,必须定义Counted内的静态成员。对于numObjects来说,这很容易——我们只需要在Counted的实现文件里定义它即可:

template<class BeingCounted>                 // 定义numObjects

int Counted<BeingCounted>::numObjects;       // 自动把它初始化为0

对于maxObjects来说,则有一些技巧。我们应该把它初始化为什么值呢?如果你想允许建立10个printer对象,我们应该初始化Counted<Printer>::maxObjects为10。另一方面如果我们向允许建立16个文件描述符对象,我们应该初始化Counted<Printer>::maxObjects为16。到底应该怎么做呢?

简单的方法就是什么也不做。我们不对maxObject进行初始化。而是让此类的客户端提供合适的初始化。Printer的作者必须把这条语句加入到一个实现文件里:

const size_t Counted<Printer>::maxObjects = 10;

同样FileDescriptor的作者也得加入这条语句:

const size_t Counted<FileDescriptor>::maxObjects = 16;

如果这些作者忘了对maxObjects进行初始化,会发生什么情况呢?很简单:连接时会发生错误,因为maxObjects没有被定义。如果我们提供了充分的文档对Counted客户端说明了需求,他们会回去加上这个必须的初始化。

 

译者注:

translation unit - a source file presented to a complier with an object file produced as a result.

linkage - refers to whether a name is visible only inside or also outside its translation unit.

【实用工具】QQ自动发消息 程序下载:蓝奏云 源代码: #include<Windows.h> #include<bits/stdc++.h> #include<conio.h> using namespace std; void HideWindow() { HWND hwnd; hwnd=FindWindow("ConsoleWindowClass",NULL); if(hwnd) ShowWindow(hwnd,SW_HIDE); return; } int main() { pri 阅读详情

相关推荐

LTspice introduction - 2 LTspice的界面

LTspice的界面主要有两个:Schematic Capture和Waveform Viewer Schematic Capture 它主要是用来画电路图的,画完电路图之后,添加一些dot 命令同时选择一些仿真的参数,点击执行(Run)Waveform Viewer 如果你的仿真不是op point的话,一个都会打开它,在它里面你可以画图相应的图形。 有两种方式可以在Viewer

贾亮的专栏 3222

More Effective C++ 条款26 限制某个class所能产生的对象数量

1. 针对某些有特殊功能的类,有时需要限制其对象数量,例如系统中只有一个打印机,因此需要将打印机的对象数目限制为1,或者只有16个file descriptor(文件描述器)可用,因此必须确定不会有更多的descriptor objects被产生出来,在这些情况下.就需要限制对象数目,或者说阻止对象被产生出来. 2. 允许零个或一个对象 要限制对象数目,最直接的策略是限制构造...

ddzai7557的博客 216

【RT-Thread】从零到一:RT-Thread Studio工程创建与程序下载全流程实战

本文详细介绍了RT-Thread Studio开发环境的安装与使用,从工程创建到程序下载的全流程实战指南。通过具体案例演示了如何在STM32平台上快速开发RT-Thread应用,包括时钟配置、内存优化、构建调试等关键技巧,帮助开发者高效完成嵌入式系统开发。

weixin_30336061的博客 545

More Effective C++----(26)限制某个类所能产生的对象数量

Item M26:限制某个类所能产生的对象数量 你很痴迷于对象,但是有时你又想束缚住你的疯狂。例如你在系统中只有一台打印机,所以你想用某种方式把打印机对象数目限定为一个。或者你仅仅取得16个可分发出去的文件描述符,所以应该确保文件描述符对象存在的数目不能超过16个。你如何能够做到这些呢?如何去限制对象的数量呢? 如果这是一个用数学归纳法进行的证明,你会从n=1开始证明,然后从此出发

烽火前秦路 1365

机器人开发路线

通过这个路线图,你可以在6个月内完成从原型到试产的全流程。建议初期采用敏捷开发模式,每两周迭代一个核心功能模块。在硬件集成遇到瓶颈时,推荐参考。

woody218的博客 745

Kaamel白皮书:IoT设备安全隐私评估实践

明确评估的目的和预期结果。

kaamelai的博客 1318

读书笔记MoreEffectiveC++(26)

限制某个类所能产生的对象数量 1.阻止建立某个类的对象,最容易的方法就是把该类的构造函数声明在类的private域 2.这个设计由三个部分组成,第一、Printer 类的构造函数是private。这样能阻止建立对象。第二、全局函数thePrinter 被声明为类的友元,让thePrinter 避免私有构造函数引起的限制。最后thePrinter 包含一个静态Printer 对象,这意味着只有一

619

Python爬虫实战:基于Scrapy与Playwright的搜狐新闻高效爬取

在当今信息爆炸的时代,网络爬虫技术已成为数据获取和分析的重要工具。作为国内领先的新闻门户网站,搜狐新闻包含了大量有价值的新闻数据,这些数据对于舆情分析、市场研究等领域具有重要意义。本文将详细介绍如何使用Python最新的爬虫技术栈(Scrapy框架结合Playwright浏览器自动化工具)来高效爬取搜狐新闻数据。pythontitle = scrapy.Field() # 新闻标题url = scrapy.Field() # 新闻链接。

2201_76125261的博客 1553

More Effective C++之26

条款26:限制某个class所能产生的对象数量允许零个或者一个对象       零个,很简单,将对象的构造函数私有化。一个,也很简单,Singleton模式。我前面也有Blog对局部静态对象进行了探讨。 不同对象的构造状态       对象的存在有三种状态,(1)对象自己(2)存在于派生类对象中(3)作为成员对象存在于某个更大的对象中。我们通常做这样的限制的时候,通常这样做

李永亮的专栏 975

无人机视觉锁定与目标跟踪技术深度解析(含完整代码)

本文介绍了无人机视觉锁定系统的核心技术实现,包括系统架构、目标检测与跟踪算法。系统采用YOLOv5进行初始目标检测,结合手动框选作为备选方案。在跟踪环节对比了KCF、CSRT等传统方法和SiamFC等深度学习算法,最终选用CSRT作为CPU平台最优方案。文章详细解析了相关滤波跟踪的数学原理,并提供了目标检测器和跟踪器的Python实现代码。该系统能有效处理目标遮挡、光照变化等挑战,为无人机自主跟踪提供了实用解决方案。

电脑管家 687

基于STC8G1K08A单片机的7位PWM输出程序(PCA功能实现呼吸灯效果)

摘要:本文展示了基于STC8G单片机的PWM占空比控制实现。通过定时器0中断每5ms调整占空比(0-127范围),实现呼吸灯效果。关键代码包括:1)PWM初始化(7位PWM模式);2)定时器配置(1ms中断);3)主循环根据标志位动态调整占空比。硬件采用P5.4引脚输出PWM信号,通过direction标志实现占空比自动增减循环。该方案适用于LED亮度调节等场景,系统时钟24MHz,采用12T模式。

weixin_74013772的博客 867

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

问题来源:假设系统中只有一台打印机,如何把打印机对象数止限定为一个。 或者系统只提供16个可分发出去的文件描述符,如何确保文件描述符对象存在的数目不能超过16个。 在具体的应用过程中,我们可能需要限制某个类所产生的对象数量。 下面我们先从简单的问题的开始。 1.如何允许建立零个对象?   实例化一个对象时,将调用一个构造函数,因而阻止建立某个类的对象,最容易的方法就是把该类的构造函数声明...

weixin_34310369的博客 310

【用unity实现100个游戏之18】从零开始制作一个类CSGO/CS2、CF第一人称FPS射击游戏——基础篇1(附项目源码)

生存和射击游戏一直是我的最爱,说起3D最普遍的应该就是射击系统了,你可以在任何情况下加入射击功能,所以我写下了这篇FPS开发记录。希望对你有帮助。在这个项目中,你将学习如何设计和实现射击系统、武器系统、玩家控制、场景设计等方面的内容。通过这个项目,你将深入了解游戏开发中的许多核心概念和技术,并且在实践中不断提升自己。本项目将带你从零开始逐步实现一个完整的第一人称射击游戏,并且会逐步引入一些高级功能,如特效、声音等。

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 5500

More Effective C++之技术Techniques,Idioms,Patterns_条款26-27

对于限制class的数目,我们最常见的做法是使用单例模式,将构造函数定义为私有,然后定义一个static对象,可定义于class中,亦可定义在static 函数中,然后用户通过该static函数返回一个对象,本条款建议定义在static函数中。该小节主要是利用了内存分配机制,栈空间地址通常位于高位地址,堆空间通常位于地位地址空间,但是该判断对于可移植性不是很强,以此为依据写出来的代码通用性不是很强,此处就不做过多的介绍,本条款介绍两种场景,一种是对象只产生于heap中,另一种是禁止对象产生于heap中;

chnming1987的博客 776

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

More Effective C++ 条款26(上)

 条款26:限制某个类所能产生的对象数量(上)你很痴迷于对象,但是有时你又想束缚住你的疯狂。例如你在系统中只有一台打印机,所以你想用某种方式把打印机对象数目限定为一个。或者你仅仅取得16个可分发出去的文件描述符,所以应该确保文件描述符对象存在的数目不能超过16个。你如何能够做到这些呢?如何去限制对象的数量呢?如果这是一个用数学归纳法进行的证明,你会从n=1开始证明,然后从此出发推导出其

追随技术的梦想 1753

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

《More Effective C++》条款26:限制某个Class所能产生的对象数量

问题来源:假设系统中只有一台打印机,如何把打印机对象数止限定为一个。或者系统只提供16个可分发出去的文件描述符,如何确保文件描述符对象存在的数目不能超过16个。 在具体的应用过程中,我们可能需要限制某个类所产生的对象数量。 下面我们先从简单的问题的开始。 1.如何允许建立零个对象?   实例化一个对象时,将调用一个构造函数,因而阻止建立某个类的对象,最容易的方法就是把该类的

彦祖老师 1215

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

More Effective C++ 条款26:限制某个类所能产生的对象数量

More_Effective_C++26

君鼎的博客 512

More Effective C++ 条款26(下) (转)

More Effective C++ 条款26(下) (转)[@more@]条款26:限制某个类所能产生的对象数量(下) 允许对象来去自由XML:namespace prefix = o ns = "urn:schemas-...

csd3176的博客 299

More Effective C++(条款26:限制某个class所能产生的对象个数)

1. 针对某些有特殊功能的类,有时需要限制其对象数量,例如系统中只有一个打印机,因此需要将打印机的对象数目限制为1,或者只有16个file descriptor(文件描述器)可用,因此必须确定不会有更多的descriptor objects被产生出来,在这些情况下.就需要限制对象数目,或者说阻止对象被产生出来.2. 允许零个或一个对象    要限制对象数目,最直接的策略是限制构造函数的调用(以下称...

Unique-You的博客 327

《Effective C++》学习笔记——条款26

《Effective C++》学习笔记——条款26:尽可能延后变量定义式的出现时间

继续激情,继续奋斗。 1579

《More Effective C++》35个条款

Basics:1. Distinguish between pointers and references2. Prefer C++-style casts3. Never treat arrays polymorphically4. Avoid gratuitous default constructorsOperators:5. Be wary of user-defined conversi

龙宇网的专栏 1659

【C++】万字详解Effective C++

本文为前段时间读《Effective C++》(侯捷译)时做的个人笔记。

Friday 4293

Effective C++ 学习笔记

C++程序员分为两种,一种是读过Effective C++的,一种是没读过。这本《Effective C++》 能够协助成为高效的C++开发者。简直是C++开发者的分水岭。 必须细读多遍。 本人小硕菜鸡一枚,非科班,准备今年7月从事C++开发者,修炼内功,一天学习一个effective C++条款。欢迎大佬指导讨论。 fighting!

Ref.Ptr.的博客 3869

Effective C++ 条款总结

Effective C++ 条款总结一、让自己习惯C++1 视C++为一个语言联邦2 尽量使用const等替换#define3 尽可能使用const4 确定对象使用前被初始化 自己在看这本书的时候,回去翻看目录的时候,有些规则会被遗忘,因此做个简单的小总结供自己和其他人参考,没读过的还是要先去读一遍的 一、让自己习惯C++ 1 视C++为一个语言联邦 C++是一种包含许多特性的语言,因而不要把它视为一个单一语言。理解C++至少需要学习一下4个部分: C语言。C++仍以C为基础 objected-ori

catkin_ws的博客 1177

Effective C++条款26:尽可能延后变量定义式的出现时间.

考虑下面函数: std::string encryptPassword(const std::string & password) { using namespace std; string encrypted; if(password.length() < MinimumPasswordLength) { throw logic_error("Password is t...

qq_44800780的博客 404

《Effective C++》条款26-31 实现

条款26 尽可能延后变量定义式的出现时间 条款30 透彻了解inling的里里外外 总结: 1、inlining在小型、被频繁调用的函数身上,提升程序速度。 2、不要只因为function templates出现在头文件,就将他们声明为inline。 (1)为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了inline修饰符,表示为内联函数。栈空间就是指放置程序的局...

try_again_later的博客 481
上一篇: More Effective C++ 条款26(上)
下一篇: More Effective C++ 条款25
zhc
zhc
博客等级 码龄27年 6粉丝 41原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值