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

More Effective C++之技术Techniques,Idioms,Patterns

  • 条款26:限制某个class所能产生的对象数量
  • 条款27:要求(或禁止)对象产生于heap之中
    • 要求对象产生于heap之中(译注:所谓Heap-Based Objects)
    • 判断某对象是否位于heap内
    • 禁止对象产生于heap中

条款26:限制某个class所能产生的对象数量

    对于限制class的数目,我们最常见的做法是使用单例模式,将构造函数定义为私有,然后定义一个static对象,可定义于class中,亦可定义在static 函数中,然后用户通过该static函数返回一个对象,本条款建议定义在static函数中。主要是为了更确切的对象构造时间(第一次调用该static function);示例代码如下:

class Printer {
public:static Printer& thePrinter();...
private:Printer();Printer(const Printer& rhs);...
};
Printer & Printer::thePrinter() {static Printer p;return p;
}

    此外,为了避免名字冲突可以将类和函数定义到命名空间中;主要核心思想还是为了解决对象顶多只产生一个对象的问题。
    本条款给出了另一种情况,对象可能需要产生多个(大于1个)分别适用于不同场景。此时使用上述的的单例模式就显得有点力不从心了;所以本条款,采用了静态变量对对象个数使用引用计数的方式,进行处理,同时限制对象的最大数进行制约;从而达到限制class所能产生的对象数目的目的。本条款,一开始直接在Printer中增加了static numberObjects及maxObjects的方式,约束对象的数目;实现后,发现可以将该逻辑定义到一个基类中,对于有相同限制需要的类,直接继承该类即可达到限制目的。

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 int maxObjects;void init();
};
template <class BeingCounted>
Counted<BeingCounted>::Counted() 
{ init(); }
template<class BeingCounted>
int Counted<BeingCounted>::numObjects;
template <class BeingCounted>
Counted<BeingCounted>::Counted(const Counted<BeingCounted>&) 
{ init(); }
template <class BeingCounted>
void Counted<BeingCounted>::init() { if (numObjects >= maxObjects) throw TooManyObjects();++ numObjects;
}

    从类的定义来看,事实上就是通过numObjects >= maxObjects的条件约束,如果满足了该条件就会抛出异常,从而限制了对象的数量。针对开头定义的Printer需要约束对象的大小:只需要做如下改动:

class Printer : private Counted<Printer> {
public:static Printer * makePrinter() { return new Printer(); }static Printer * makePrinter(const Printer& job) { return new Printer(job); } void reset();...using Counted<Printer>::objectCount;using Counted<Printer>::TooManyObjects;
private:Printer();Printer(const Printer& rhs);
};

    此处Printer只需集成字Counted<Printer>即可完成对类对象的数量;实际数量可通过

template<>
const int Counted<Printer>::maxObjects = 1;

    注意该语句必须指定,否则会导致链接失败;比如实际需要限制的数量为10,则可修改为

template<>
const int Counted<Printer>::maxObjects = 10;

条款27:要求(或禁止)对象产生于heap之中

    本条款介绍两种场景,一种是对象只产生于heap中,另一种是禁止对象产生于heap中;从实现细节上来看,实现方法都会存在一定的瑕疵,权当做一种借鉴方法。

要求对象产生于heap之中(译注:所谓Heap-Based Objects)

    因为non-heap objects会在其定义点自动构造,并在跳出有效范围后自动析构,所以我们只要让那些被隐式调用的构造动作或析构动作不合法,就可以了。
    为了使这些动作不合法,最直截了当的方式是将constructor和destructor声明为private。事实上,没必要把它们俩都声明为private,比较好的办法是让destructor成为private,而constructor仍为public。如此一来,我们可以导入一个pseudo(伪的)destructor函数,用来调用真正的destructor。Clients则调用这个psedu-destructor以销毁它们所产生的对象。
    示例代码如下:

class Base {
public:Test();...// pseudo destructorvoid destroy() const { delete this; }
private:~Base();
};

    使用Base类,于是这么写:

Base test;							// 编译报错,因为析构函数为private
Base *pTest = new Base;
...
delete pTest;						// 编译错误,企图调用private destructor
p->destroy();						// 良好。

    另一个办法是将constructors都声明为private;但是由于编译器会默认产生copy constructor,default constructor且为public,如此代码需要把所以这些默认的构造函数都罗列在private下,所以没有把destructor声明为private来得简洁。因为一个class只能有一个destructor。
    只要限制了destructor或constructor的运用,便可阻止non-heap objects的诞生。但是,它也妨碍而来继承(inheritance)和内含(containment):

class Inhr : public Base {
public:Inhr() : Base(){}
};
class Comp {
private:Base base;
};

    以上代码会出现编译错误,在class Inhr因为Base的构造函数为private,导致函数调用destroy内部调用~Base会失败;将destructor修改为protected即可:

class Base {
public:Base() {}// pseudo destructorvoid destroy() const { delete this; }
protected:~Base() {}
};class Inhr : public Base {
public:Inhr() : Base(){}
};

    对于包含元素则使用包含指针的方式进行处理:

class Comp {
public:Comp() {test = new Base;}~Comp() {test->destroy();}
private:Base *test;
};

判断某对象是否位于heap内

    该小节主要是利用了内存分配机制,栈空间地址通常位于高位地址,堆空间通常位于地位地址空间,但是该判断对于可移植性不是很强,以此为依据写出来的代码通用性不是很强,此处就不做过多的介绍,

禁止对象产生于heap中

    “检验对象是否位于heap中”的判断不是很清晰。“阻止对象分配于heap中”则是另一种手段。为了实现该目的,方案相对清晰。一般而言有3种可能:(1)对象被直接实例化;(2)对象被实例化为derived class objects内的“base class成分”;(3)对象内嵌于其他对象之中。让我们一一讨论。
    欲阻止clients直接将对象实例化于heap之中,很容易,因为此等对象总是以new产生出来,我们可以让clients无法调用new。虽然不能影响new operator的能力,但我们可以利用一个事实:new operator总是调用 operator new,而后者是我们可以自行声明的。更明确地说,我们可以将它声明为private或protected。示例代码如下:

class Base {
public:Base() {}
private:static void* operator new (size_t size) ;static void operator delete (void* ptr);
};

    现在clients只能够做某些被允许的事情

Base base;															// 可以
static Base sBase;													// 也可以
Base *pBase = new Base;						   						// 编译错误!企图调用private operator new

    将operator new声明为private应该足够了,但如果operator new属性为private 而operator delete却为public,看上去略奇怪,所以把它们俩都声明为了private。此外,为了禁止new Base[]的调用,我们同样需要把operator new[]及operator delete[]声明为private。
    有趣的是,将operator new声明为private,往往也会妨碍Base被实例化为heap-base derived class objects的“base class 成分”。那是因为operator new和operator delete都会被继承,所以如果这些函数不在derived class内声明为public,derived class继承了base(s)所声明的private版本:

class D : public Base {
};
D d;								// 没问题
static D sd;						// 没问题
D *p = new D;						// 错误!企图调用private operator new

    如果derived class声明有一个属于自己的operator new且为public,当clients将derived class objects声明于heap内时,该operator new函数会被调用,因此我们必须另觅良方以求阻止“D的Base class成分”的诞生。类似情况,当我们企图分配一个“内含Base对象”的对象,“Base的operator new乃为private”这一事实并不会带来什么影响:

class X {
Base base;
};
X *pX = new X;						// 没问题,调用的是X::operator new而非Base::operator new

    对使用目的而言,这把我们带回熟悉的情境。我们曾经希望“如果一个Base对象构造于heap以外,那么Base constructor就抛出exception”,这次我们的希望是“如果对象被产生于heap内的话,就抛出一个exception”。然而,就像没有任何据可移植性的做法可以判断某地址是否位于heap内一样,我们也没有据移植性的做法可以判定它是否不在heap内。这应该不令人惊讶;
    由此,本条款,给了我们两个一定程度上解决要求(或禁止)对象产生于heap之中。因为,clients可以通过友元、函数重载或者成员变量的方式绕过以上定义的private/protected定义,从而使通过以上方法的限制失效。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/495485.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AIA - IMSIC之二(附IMSIC处理流程图)

本文属于《 RISC-V指令集基础系列教程》之一,欢迎查看其它文章。 1 ​​​​​​​通过IMSIC接收外部中断的CSR 软件通过《AIA - 新增的CSR》描述的CSR来访问IMSIC。 machine level 的 CSR 与 IMSIC 的 machine level interrupt file 可相互互动;而 supervisor level 的 CSR…

光谱相机的工作原理

光谱相机的工作原理主要基于不同物质对不同波长光的吸收、反射和透射特性存在差异&#xff0c;以下是其具体工作过程&#xff1a; 一、光的收集 目标物体在光源照射下&#xff0c;其表面会对光产生吸收、反射和透射等相互作用。光谱相机的光学系统&#xff08;如透镜、反射镜…

Kafka可视化工具 Offset Explorer (以前叫Kafka Tool)

数据的存储是基于 主题&#xff08;Topic&#xff09; 和 分区&#xff08;Partition&#xff09; 的 Kafka是一个高可靠性的分布式消息系统&#xff0c;广泛应用于大规模数据处理和实时, 为了更方便地管理和监控Kafka集群&#xff0c;开发人员和运维人员经常需要使用可视化工具…

TLDR:终端命令的简洁百科全书

TLDR&#xff0c;全称 “Too Long, Don’t Read”&#xff0c;是一款特别实用的终端命令百科全书工具。通过 TLDR&#xff0c;您可以快速查找到常用命令的使用方法&#xff0c;避免繁琐冗长的官方文档&#xff0c;让日常工作更加高效。 为什么选择 TLDR&#xff1f; 简单易用&…

2024-12-25-sklearn学习(20)无监督学习-双聚类 料峭春风吹酒醒,微冷,山头斜照却相迎。

文章目录 sklearn学习(20) 无监督学习-双聚类1 Spectral Co-Clustering1.1 数学公式 2 Spectral Biclustering2.1 数学表示 3 Biclustering 评价 sklearn学习(20) 无监督学习-双聚类 文章参考网站&#xff1a; https://sklearn.apachecn.org/ 和 https://scikit-learn.org/sta…

数据结构(Java版)第六期:LinkedList与链表(一)

目录 一、链表 1.1. 链表的概念及结构 1.2. 链表的实现 专栏&#xff1a;数据结构(Java版) 个人主页&#xff1a;手握风云 一、链表 1.1. 链表的概念及结构 链表是⼀种物理存储结构上⾮连续存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的引⽤链接次序实现的。与火车…

《Java核心技术I》Swing的网格包布局

复杂的布局管理 网格包布局 行列大小可改变&#xff0c;先建立表格&#xff0c;合并相邻单元格&#xff0c;组件指定在格内的对齐方式。 字体选择器组件&#xff1a; 另个指定字体和字体大小的组合框两个组合框标签两个选择粗体和斜体的复选框一个显示示例字符串的文本区 将容…

Python——day09

os模块 sys模块 time模块 logging模块

IIC驱动EEPROM

代码参考正点原子 i2c_dri:主要是三段式状态机的编写 module iic_dri#(parameter SLAVE_ADDR 7b1010000 , //EEPROM从机地址parameter CLK_FREQ 26d50_000_000, //模块输入的时钟频率parameter I2C_FREQ 18d250_000 //IIC_SCL的时钟频率)( …

《计算机组成及汇编语言原理》阅读笔记:p86-p115

《计算机组成及汇编语言原理》学习第 6 天&#xff0c;p86-p115 总结&#xff0c;总计 20 页。 一、技术总结 1.if statement 2.loop 在许多编程语言中&#xff0c;有类种循环&#xff1a;一种是在程序开头检测条件(test the condition),另一种是在程序末尾检测条件。 3.C…

(带源码)宠物主题商场系统 计算机项目 P10083

项目说明 本号所发布的项目均由我部署运行验证&#xff0c;可保证项目系统正常运行&#xff0c;以及提供完整源码。 如需要远程部署/定制/讲解系统&#xff0c;可以联系我。定制项目未经同意不会上传&#xff01; 项目源码获取方式放在文章末尾处 注&#xff1a;项目仅供学…

目标检测——基于yolov8和pyqt的螺栓松动检测系统

目录 1.项目克隆和环境配置1.1 我这里使用的是v8.0.6版本1.2 项目代码结构介绍 2.数据集介绍2.1 数据集采集2.2采集结果介绍 3.模型训练4.pyqt界面设计4.1 界面内容介绍4.2 界面实现 5.操作中的逻辑实现5.1 图片检测5.2 文件夹检测5.3 视频检测和摄像头检测 6. 效果展示 1.项目…

宠物行业的出路:在爱与陪伴中寻找增长新机遇

在当下的消费市场中&#xff0c;如果说有什么领域能够逆势而上&#xff0c;宠物行业无疑是一个亮点。当人们越来越注重生活品质和精神寄托时&#xff0c;宠物成为了许多人的重要伴侣。它们不仅仅是家庭的一员&#xff0c;更是情感的寄托和生活的调剂。然而&#xff0c;随着行业…

原点安全再次入选信通院 2024 大数据“星河”案例

近日&#xff0c;中国信息通信研究院和中国通信标准化协会大数据技术标准推进委员会&#xff08;CCSA TC601&#xff09;共同组织开展的 2024 大数据“星河&#xff08;Galaxy&#xff09;”案例征集活动结果正式公布。由工银瑞信基金管理有限公司、北京原点数安科技有限公司联…

【0x001D】HCI_Read_Remote_Version_Information命令详解

目录 一、命令概述 二、命令格式及参数说明 2.12. HCI_Read_Remote_Version_Information 命令格式 2.2. Connection_Handle 三、生成事件 3.1. HCI_Command_Status 事件 3.2. HCI_Read_Remote_Version_Information_Complete 事件 四、命令执行流程 4.1. 命令发起阶段(…

C语言-结构体内存大小

#include <stdio.h> #include <string.h> struct S1 { char a;//1 int b;//4 char c;//1 }; //分析 默认对齐数 成员对齐数 对齐数(前两个最小值) 最大对齐数 // 8 1 …

直流电源如何输出恒压源和恒流源

输出电流达到预定值时&#xff0c;变成稳流特性。 输出电压达到预定值时&#xff0c;变成稳压特性。 电流变大&#xff0c;成稳压。 电压变大&#xff0c;成稳流。

【软考高级】系统架构设计师复习笔记-精华版

文章目录 前言0 系统架构设计师0.1 考架构还是考系分0.2 架构核心知识0.3 架构教材变化 1 计算机操作系统1.1 cpu 组成1.2 内核的五大功能1.3 流水线技术1.4 段页式存储1.5 I/O 软件1.6 文件管理1.7 系统工程相关 2 嵌入式2.1 嵌入式技术2.2 板级支持包&#xff08;BSP&#xf…

如何识别钓鱼邮件和诈骗网站?(附网络安全意识培训PPT资料)

识别钓鱼邮件和诈骗网站是网络安全中的一个重要环节。以下是一些识别钓鱼邮件和诈骗网站的方法&#xff1a; 识别钓鱼邮件&#xff1a; 检查发件人地址&#xff1a; 仔细查看发件人的电子邮件地址&#xff0c;看是否与官方域名一致。 检查邮件内容&#xff1a; 留意邮件中是否…

查询 MySQL 默认的存储引擎(SELECT @@default_storage_engine;)

要查询 MySQL 默认的存储引擎&#xff0c;可以使用以下 SQL 查询语句&#xff1a; SELECT default_storage_engine;解释&#xff1a; SELECT: 表示你要执行一个查询。default_storage_engine: 这是一个 MySQL 系统变量&#xff0c;它存储着当前 MySQL 服务器的默认存储引擎。…