当前位置: 首页 > news >正文

条款39:明智而审慎地使用private继承

1.前言

在之前挑款32曾讨论了C++如何将public继承视为is-a关系,在那个例子中我们有个继承体系,其中class Student以public形式继承class Person,于是编译器在必要时刻将Student转换为Persons。。现在,我在以原先那个例子,以private继承替换public继承:

class Person{...};
class Student:private Person{...};//这次改为private继承
void eat(const Person& p);//任何人都会吃
void study(const Student& s);//只有学生才在校学习
Person p;//p是人
Student s;//s是学生
eat(p);//没问题,p是人,会吃
eat(s);//错误

该例子显然表现了private继承并不意味着is-a关系。

在我们继续探讨之前,要先搞清楚到底private继承地行为是如何地。private继承地首要规则刚才已经说过:如果class之间地继承关系是private,编译器不会自动将一个derived class对象转换为一个base classes对象。这和public继承地情况不同。这也是为什么通过s调用eat会失败地原因。第二条规则是由private base class继承而来地所有成员,在derived class中都会变成private属性,纵使它们在base class中原本是protected或public属性。

2.实例分析

private继承意味着implemented-in-terms-of(根据某事实现出)。如果你让class D以private形式继承class B,用意是为了采用class B内已经具备某些特性,不是因为B对象和D对象存在任何观念上地关系。private继承只是一种实现技术(这就是为什么继承自一个private base class的每样东西在你的class内都是private:因为它们都是实现的细节而已)。借用条款34提出的术语,private继承意味只有实现部分被继承,接口部分应省略。如果D以private形式继承B,意思是D对象根据B对象实现而已,再也没有其它意义。Private继承在软件的设计层面没有意义,其意义只关系到软件实现层面。

private继承意味着is-implemented-in-terms-of(根据某物实现出),这个事实有点令人不安,因为在条款39中的复合也是这样的意义。要在两者之间实现取舍,做法是:尽可能使用复合,必要时才使用private继承。那么何时需要呢?主要是当protected成员或virtual函数牵扯进来的时候。

假设我们的程序涉及Widgets,而我们决定应该较好的了解如何使用Widgets。例如我们不只想要知道Widget成员函数多么频繁地被调用,也想知道经过一段时间后调用比例是如何变化。

我们决定修改Widget class,让它记录每个成员函数地调用次数。运行期间我们将周期性地审查那份信息,再加上每个Widget地值,以及我们需要评估地任何其它数据。为完成这项工作,我们需要设定某种定时器,使我们知道收集统计数据地时候到了。见以下例子:

class Timer{public:explicit Timer(int tickFrequency);virtual void onTick() const;//定时器每滴答一次,此函数就被调用一次
}

一个Timer对象,可调整为我们需要地任何频率前进,每次滴答就调用某个virtual函数。我们可以重新定义那个virtual函数,让后者得到Widget当时地状态。

为了让Widget重新定义Timer内地virtual函数,widget必须继承自Timer。但public继承在此例并不合适,因为Widget并不是个Timer。Widget客户纵不能够对着一个Widget调用onTick,因为观念上那并不是Widget接口地一部分。如果允许那样地调用动作,很容易造成客户不正确地使用Widget接口。所以这里我们以private形式继承Timer:

class Widget:private Timer{private:virtual void onTick() const;//查看Widget的数据等    ...
};

由private继承,Timer的public onTick函数在Widget内变成private,而我们重新声明时仍然把它留在那。再强调一次,把onTick放进public接口内会误导客户端调用,违反了条款18。

这是个好设计,但并不值得推崇。因为private继承并非绝对必要,如过我们决定1以复合(composition)取而代之,也是可以完成该项任务的。只要在Widget内声明一个嵌套式private class,后者以public形式继承Timer并重新定义onTick,然后放一个这种类型的对象于Widget内,具体例子如下:

class Widget{private:class WidgetTimer:public Timer{public:virtual void onTick() const;...};WidgetTimer timer;...
};

该设计只比使用private继承复杂一些,因为它同时涉及public继承和复合,并导入了一个新的class。

3.总结

(1)private继承意味着is-implemented-in-terms of(根据某事实现出)。它通常比复合(conposition)的级别低。但是当derived class需要访问protected base class的成员,或需要重新定义继承而来的virtual函数时,这个设计是合理的。

http://www.lryc.cn/news/278239.html

相关文章:

  • 【数据库原理】(20)查询优化概述
  • FineBI实战项目一(18):每小时上架商品个数分析开发
  • Pytorch常用的函数(六)常见的归一化总结(BatchNorm/LayerNorm/InsNorm/GroupNorm)
  • 业务记录笔记
  • Leetcode16-有多少小于当前数字的数字(1365)
  • JavaWeb- Tomcat
  • Android studio 各本版下载
  • [C#]winform部署PaddleOCRV3推理模型
  • 谈谈Spring Bean
  • kubernetes(一)概述与架构
  • 【Scala】——变量数据类型运算符
  • 嵌入式培训机构四个月实训课程笔记(完整版)-Linux系统编程第十天-Linux下mplayer音乐播放器练习题(物联技术666)
  • 线性回归(Linear Regression)
  • matlab绘图修改坐标轴数字字体大小及坐标轴自定义间隔设置
  • C++入门教程,C++基础教程(第一部分:从C到C++)七
  • 【数据库】视图索引执行计划多表查询笔试题
  • CentOS7本地部署分布式开源监控系统Zabbix并结合内网穿透实现远程访问
  • 虚拟主机 如何上传大于100M的文件 php网站程序
  • 登录模块的实现
  • Asp .Net Core系列:基于MySQL的DBHelper帮助类和SQL Server的DBHelper帮助类
  • 【排序】对各种排序的总结
  • Apache ActiveMQ RCE CNVD-2023-69477 CVE-2023-46604
  • C语言可变参数输入
  • 飞天使-k8s知识点10-kubernetes资源对象3-controller
  • 【Vue技巧】Vue2和Vue3组件上使用v-model的实现原理
  • 博客随手记
  • 【2023】java常用HTTP客户端对比以及使用(HttpClient/OkHttp/WebClient)
  • 微信小程序获取来源场景值
  • Vue3:vue-cli项目创建及vue.config.js配置
  • 关于CAD导入**地球的一些问题讨论