Effective C++条款38:通过复合塑模出 has-a 或“根据某物实现出“

news2025/1/13 17:30:22

Effective C++条款38:通过复合塑模出 has-a 或"根据某物实现出"(Model "has-a" or "is-implemented-in-terms-of" through composition)

  • 条款38:通过复合塑模出 has-a 或"根据某物实现出"
    • 1、什么是复合(composition)?
    • 2、“is-a”和“has-a”
    • 3、“is-a”和“is-implemented-in-terms-of”
    • 3、牢记
  • 总结


《Effective C++》是一本轻薄短小的高密度的“专家经验积累”。本系列就是对Effective C++进行通读:

第6章:继承与面向对象设计

在这里插入图片描述


条款38:通过复合塑模出 has-a 或"根据某物实现出"

1、什么是复合(composition)?

  复合(composition)是类型之间的一种关系,这种关系当一种类型的对象包含另外一种类型的对象时就会产生。例如:

class Address { ... };      // 某人额住址
class PhoneNumber { ... };
class Person {
	public:
	...
private:
	std::string name;         	// 合成成分物
	Address address; 			// 同上
	PhoneNumber voiceNumber; 	// 同上
	PhoneNumber faxNumber;     // 同上
};   

  在这个例子中,Person对象由string,Address和PhoneNumber对象构成。在程序员之间,复合(composition)有很多同义词。像分层(layering),包含( containment),聚合 (aggregation), 和植入(embedding)。

2、“is-a”和“has-a”

  条款32中解释了public继承意味着”is-a”。复合也有它自己的意思。实际上,有两个意思。复合意味着“has-a”或“is-implemented-in-terms-of”(根据某物实现出)。这是因为你正在处理软件中的两种不同领域(domain)。你的程序中的一些对象对应着世界上的真实存在的东西,如果你要为其建模,例如,人,汽车,视频画面等等。这样的对象是应用域(application domain)的一部分。其他的对象则是纯实现层面的人工制品。像缓存区(buffers),互斥器(mutexs),查找树(search trees)等。这些对象对应着你软件里的实现域(implementation domain)。当复合关系发生在应用域中的对象之间时,它表示的是“has-a”关系。当发生在实现域中时,它表示的是“is-implemented-in-terms-of”关系。

  上面的Person类表示的是一种“has-a”关系。一个Person对象有一个名称,一个地址以及语音和传真电话号码。你不能说一个人“is-a”名字或者一个人“is-a”地址。你会说人“has-a”名字和“has-an”地址。大多数人能够很容易区分这些,因此很少有人会混淆“is-a”和“has-a”的意思。

3、“is-a”和“is-implemented-in-terms-of”

  比较麻烦的是对“is-a”和“is-implemented-in-terms-of”进行区分。举个例子,假设你需要一个类模板表示很小的对象set,也即是没有重复元素的collections。由于复用是美好的事情,你的第一直觉就是使用标准库的set模板。当有一个已经被实现好的模板时你为什么要自己手动实现一个呢?

  不幸的是,set的实现对于其中的每个元素都会引入三个指针的开销。因为set通常作为一个平衡查找树来实现,这能保证将搜索,插入和删除的时间限定在对数级别(logarithmic-time)。当速度比空间重要时,这是个合理的设计,但是对于你的应用,空间比速度要更重要。所以标准库的set没有为你的应用提供正确的权衡。你需要自己实现这个模板。

  复用仍然是美好的事情。如果你作为数据结构专家,你就会知道实现set的方法太多了,其中一个是在底层使用linked lists。你同样知道标准C++库有一个list模板,所以你决定复用它。

  更明确的说,你决定让你的初步实现的set模板继承list。也即是Set将会继承list。毕竟,在你的实现中,一个Set对象事实上是一个list对象。所以你将Set模板声明为如下:

template<typename T>                     
class Set: public std::list<T> { ... };     

  每件事看上去都很好,但实际上有些东西完全错误。正如条款32中解释的,如果D 是一个B,对于B来说是真的对D来说也是真的。然而,一个list对象可能会包含重复元素,所以如果值3051被插入到Set两次,那么list将会包含3051的两个拷贝。相反。一个Set不可以包含重复元素,所以当3051被插入到Set两次的时候,set只包含一个3051值。现在一个Set是一个List就不再为真了,因为对list对象为真的一些事情对Set对象来说不为真了。

  因为这两个类之间的关系不是“is-a”,public继承是为这种关系塑模的错误方式。正确的方式是意识到一个Set对象可以被“implemented in terms of”一个list对象:

template<class T>                           
class Set {                                        
public:                                            
	bool member(const T& item) const; 
	void insert(const T& item);            
	void remove(const T& item);         
	std::size_t size() const;                   
private:                                          
	std::list<T> rep;              
};   

  Set成员函数的实现可以依赖list已经提供的功能和标准库的其他部分,所以实现上就简单直接了,前提是你对STL编程的基本知识很熟悉:

template<typename T>
bool Set<T>::member(const T& item) const
{
	return std::find(rep.begin(), rep.end(), item) != rep.end();
}
template<typename T>
void Set<T>::insert(const T& item)
{
	if (!member(item)) rep.push_back(item);
}
template<typename T>
void Set<T>::remove(const T& item)
{
	typename std::list<T>::iterator it = // see Item 42 for info on
	std::find(rep.begin(), rep.end(), item); // “typename” here
	if (it != rep.end()) rep.erase(it);
}
template<typename T>
std::size_t Set<T>::size() const
{
	return rep.size();
}

  这些函数足够简单,它们是inline函数的合理候选人。

3、牢记

  • 复合的意义和public继承完全不同。

  • 在应用域,复合意为has-a(有一个)。在实现域,复合意味着is-implemented-in-terms-of(根据某物实现出)。

总结

期待大家和我交流,留言或者私信,一起学习,一起进步!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Batch Normalization

1、原理 在图像预处理过程中会对图像进行标准化处理&#xff0c;这样能够加速网络的收敛速度。 如下图所示&#xff0c;对于Conv1来说输入的是满足某一分布的特征矩阵&#xff0c;但对于Conv2来说输入的feature map就不一定满足某一分布规律。 Batch Normalization的目的就是使…

大坝安全在线监控系统包含哪些内容?怎样提升水库大坝信息化管理水平?

平升电子大坝安全在线监控系统根据SL551-2012《土石坝安全监测技术规范》的整编要求&#xff0c;设置了变形监测、渗流监测、环境量监测。大坝安全在线监控系统可及时了解大坝的工作性态和水库可能存在的事故隐患&#xff0c;为大坝安全管理与水库运行调度提供了准确、及时的现…

Elasticsearch集群搭建

前言在如今的开发过程中&#xff0c;单节点的Elasticsearch肯定是支撑不了大数据量的&#xff0c;而且还存在单节点故障的问题&#xff0c;所以Elasticsearch也提供了集群功能&#xff0c;像其他中间件也基本都会考虑到这个问题准备信息首先&#xff0c; 由于我机器有限&#x…

如何在虚拟机上安装Linx系统

前言作为Java开发的我们&#xff0c;可能有时候想自己玩玩linux服务器&#xff0c;但是如果买阿里云或者腾讯云的服务器又很贵&#xff0c;这时候我们就可以在自己电脑上安装虚拟机了&#xff0c;这也是本篇文章出现的原因&#xff0c;下面我就安装centOS7为例子来进行介绍首先…

openEuler资源利用率提升之道 05:虚机混部介绍与功耗管理技术

随着云计算市场规模的快速增长&#xff0c;各云厂商基础设施投入也不断增加&#xff0c;但行业普遍存在资源利用率低的问题&#xff0c;在上述背景下&#xff0c;提升资源利用率已经成为了一个重要的技术课题。将业务区分优先级混合部署(下文简称混部)是典型有效的资源利用率提…

【C语言】使用C语言实现静态、动态的通讯录(简单易懂)

我们在学习结构体之后&#xff0c;就可以尝试去实现通讯录的制作&#xff0c;如果您这边对于结构体还没有太多的认识的话&#xff0c;请先访问这一篇文章,会有利于接下来的学习。【自定义类型】带你走进结构体、枚举、联合_小王学代码的博客-CSDN博客 目录 一、通讯录 二、静…

JVM-【面试题】-对象内存分配

一、对象内存分配流程图如果能在栈分布就直接在栈创建如果是大对象就直接在old区创建如果不大于TLAB则在TLAB创建&#xff0c;否则在Eden区创建如果Eden区空间不足就会发生Minor GC进行回收&#xff0c;回收的空间放不下或年龄达到上限就直接放到Old区&#xff0c;之后S0区的存…

Vue3.0 性能提升主要是通过哪几方面体现的?

一、编译阶段 回顾Vue2&#xff0c;每个组件实例都对应一个 watcher 实例&#xff0c;它会在组件渲染的过程中把用到的数据property记录为依赖&#xff0c;当依赖发生改变&#xff0c;触发setter&#xff0c;则会通知watcher&#xff0c;从而使关联的组件重新渲染 试想一下&…

34420A万用表

18320918653 34420A Agilent 34420A 七位半台式数字万用表|安捷伦纳伏表34420A|微欧表|安捷伦34420A 品牌&#xff1a; Agilent(安捷伦) 1.3nV rms噪声/8n Vp-p 100pV&#xff0c;100nΩ灵敏度 两通道可编程电压输入&#xff1a;差分和比值功能 71/2位分辨率 1mV到100V量程…

如何通过指令控制将一副扑克牌变成一种简单的计算机

题目 题目就是文章的标题 已知 牌数&#xff1a;每套扑克牌有54张&#xff0c;其中去掉大小王&#xff0c;剩下52张&#xff0c;这52张中又有4种花色黑桃&#xff0c;红桃&#xff0c;梅花&#xff0c;方片每种花色都有13张&#xff08;1,2,3,4,5,6,7,8,9,10,J,Q,K&#xff…

【Ubuntu】在VMWare虚拟机中安装Ubuntu【教程】

文章目录【Ubuntu】在VMWare虚拟机中安装Ubuntu教程一、安装VMWare二、下载Ubuntu的iso文件三、安装UbuntuReference【Ubuntu】在VMWare虚拟机中安装Ubuntu教程 一、安装VMWare 具体的安装方法&#xff0c;可以参考这一篇博客&#xff0c;这里就不详细介绍了 二、下载Ubuntu…

centos禁止root登录

ssh登录linux服务器的时候&#xff0c;经常会有提示 There were * failed login attempts since the last successful login. 说明有大量的非法登录尝试 检查服务器是否被恶意登录 # Ubuntu # 1. 查看近期成功的密码登录&#xff1a; grep "password" /var/log/au…

如何搭建邮箱服务器?mail系统架设的两种方法

邮件mail通信是常用的办公场景&#xff0c;对于技术和网管等人员&#xff0c;往往需要搭建自己的邮箱服务器。那么&#xff0c;如何架设邮箱系统呢&#xff1f;通常有两种方案&#xff0c;一种是在在本地主机部署&#xff0c;另一种是在云端如云服务器上部署应用。根据主机IP情…

以交互方式导入图像、音频和视频

以交互方式将数据导入到 MATLAB 工作区。 查看文件的内容 指定变量 生成可重用的 MATLAB 代码 注意&#xff1a;​有关导入文本文件的信息&#xff0c;可以参考使用导入工具读取文本文件数据。有关导入电子表格的信息&#xff0c;可以参考使用导入工具读取电子表格数据。 查…

【Redis数据对象与结构】string与其底层结构

【Redis数据对象与结构】string与其底层结构 【Redis数据对象与结构】系列的主线如下&#xff0c;本文主要讲解string数据对象及其底层结构在redis中的实现。 redis中基本的数据对象有字符串类型(String)、列表类型(List)、字典类型(Hash)、集合类型(Set)、有序列表类型(Sorte…

自定义类型之枚举与联合

文章目录前言一、枚举1.枚举的定义2.枚举的几种情况3.枚举类型的大小4.枚举的优点二、联合&#xff08;共用体&#xff09;1.联合类型的定义2.联合的特点3.联合的大小计算总结前言 自定义类型很多人可能只知道结构体&#xff0c;因为结构体相对来说确实用的比较多&#xff0c;而…

爬虫攻守道 - 2023最新 - 正则表达式勇猛精进 - 爬取某天气网站历史数据

前言 在 正则表达式 - 匹配开头、结尾、中间 - 某天气网站网页源代码分析 这篇文章里&#xff0c;我们介绍了如何用正则表达式匹配包含特定样式的Table标签&#xff0c;也就是同时匹配开头、结尾、以及中间。 当你能真正理解这个写法&#xff0c;就会觉得不过是柳暗花明罢了。…

如何把拍摄视频中多余的人或物去除?

大家应该都有这样一个烦恼吧&#xff1f;就是拍摄的一段视频中有多余的人物出现&#xff0c;想要把里面的人物去除掉&#xff0c;或者是自己拍摄的一段视频&#xff0c;视频里出现了多余的人物&#xff0c;但是又不能重启拍摄的情况下&#xff0c;想要把视频中的人物去除掉应该…

Spring Security笔记

创建个项目 引入Spring Web和Spring Security 即可 写个Controller接收请求 转发重定向都可以 static下定义两个页面 login.html页面 用来登录 main.html如果可以跳到这里,说明登录成功 启动运行程序 我们访问登录接口 或者是访问静态资源都会重定向到这个页面 这个页面说…

并发编程(多线程)

一、进程与线程 多进程编程已经能够解决并发编程的问题了(已经可以利用cpu多核资源了).但是仍然存在这缺陷. 就是,进程太重了(消耗资源多,速度慢),线程应运而生被称为"轻量级编程",解决并发编程的各种问题的同时,让IO速度大大提升. 线程"轻"主要"轻…