Effective_C++_条款三十九:明智而审慎地使用private继承

2024-01-20 20:38

本文主要是介绍Effective_C++_条款三十九:明智而审慎地使用private继承,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

private继承的意义在于“be implemented in turns of”,这个与上一条款中说的复合模型的第二层含义是相同的,这也意味着通常我们可以在这两种设计方法之间转换,但书上还是更提倡使用复合来进行类的设计

private继承与public的继承是完全不同的,主要体现在两个地方:

其一,public继承在子类中保持父类的访问权限,即父类中是public的成员函数或成员变量,在子类中仍是public,对private或者protected的成员函数或成员变量亦是如此;但private继承则不是这样了,它破坏了父类中的访问权限标定,将之都转成private,这对子类本身并无影响(照常访问),但却影响了子类的子类,子类的子类将无法访问这些声明/定义在爷爷辈类的成员变量或成员函数。

其二,Liskov法则(替换法则)不再适用,也就是说“一切父类出现的地方都可以被子类所替代”的法则在private这里不成立,请看下面的例子(来源自书上):

#include <iostream>
using namespace std;class Person
{};class Student : private Person
{};void eat(const Person& p){}int main()
{Person p;Student s;eat(p); // OKeat(s); // 编译报错:error C2243: “类型转换”: 从“Student *”到“const Person &”的转换存在,但无法访问
}

 

但如果令Student公有继承Person,则编译器不会报错。这正是Liskov的可替代原则在private继承中不适用的体现。

 

private继承使用的地方实在不多,除非有一些奇葩的设计需求,书上说了一个例子:

class TypeDefine
{};class SimpleClass
{int a;TypeDefine obj;
};class SimpleDerivedClass : private TypeDefine
{int a;
};int main()
{cout << sizeof(TypeDefine) << endl; // ?cout << sizeof(SimpleClass) << endl; // ?cout << sizeof(SimpleDerivedClass) << endl; // ?
}

大家可以想一下“?”处的输出是什么。第一个是空类,空类就像是空气一样,仅仅是名字里面包含了“空”字,看起来是“空”的,但其实不是这样子的,空气里面混合了氧、氮、二氮化碳等气体,还有各种微生物,而对于空类,编译器会为之生成四个默认的函数:默认构造函数,默认拷贝构造,默认析构函数,默认赋值运算符。读者就会问了,编译器生成了默认的函数不假,但函数是不占空间的,为什么空类的sizeof算出的值是1?原来类的每一个对象都需要一个独一无二的内存地址,所以编译器会在空类对象中插入一个1字节变量,正是这个1字节的变量,才能够区分空类的不同对象。非空类因为已经有了成员变量,所以编译器可以利用这些成员变量来进行内存地址的区分,从而标识类的不同对象,这个时候是不需要插入一个1字节的变量的。所以第一个问号处输出的是1。

第二个问号输出的是5吗?int四字节再加到空类对象的四字节?理论上是这样,但编译器还会做一种内存对齐的操作,使得类对象的大小会是处理字长的整数倍,一般是4字节的整数倍,所以最后的结果其实是8。

第三个问号呢?前面讲的那么多,好像都与private无关,这个问题终于与它有关了。运行下看看,结果是4。为什么用复合模型时输出的结果是8,但private继承时却是4呢?这其实是编译器做了空白基类优化(EBO),原本是要为空白类对象插入1字节的,但因为子类中已经有了对象了,这样理论上就可以凭借这个对象来进行同一个类不同对象间的识别了,所以这时候编译器就不再插入字节了。

这个结果就是用private继承的好处,是不是很奇葩呢~所以我说,在大部分情况下,都不会考虑private继承,因为它的含义be implemented in terms of 可以用复合来替换。

 

书上还提到了关于虚函数不想被子类的子类所覆写的问题,这时候不能用private限制虚函数,因为生成的虚指针是一直会被继承下去的,解决方法就是用复合,而且复合的类是一个临时类且复合对象标记为private,这样就只能限制在这个类本身去覆写了。具体的例子可以去看原书。

 

最后总结一下:

1. Private继承意味着is implemented in terms of,它通常比复合的级别低(即优先使用复合),但是当derived class需要访问protected base class的成员,或需要重新定义继承而来的virtual函数时,这么设计是合理的。

2. 与复合不同,private继承可以造成empty base最优化。这对致力于“对象尺寸最小化”的程序库开发者而言,可能很重要。

这篇关于Effective_C++_条款三十九:明智而审慎地使用private继承的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/627263

相关文章

使用Dify访问mysql数据库详细代码示例

《使用Dify访问mysql数据库详细代码示例》:本文主要介绍使用Dify访问mysql数据库的相关资料,并详细讲解了如何在本地搭建数据库访问服务,使用ngrok暴露到公网,并创建知识库、数据库访... 1、在本地搭建数据库访问的服务,并使用ngrok暴露到公网。#sql_tools.pyfrom

使用mvn deploy命令上传jar包的实现

《使用mvndeploy命令上传jar包的实现》本文介绍了使用mvndeploy:deploy-file命令将本地仓库中的JAR包重新发布到Maven私服,文中通过示例代码介绍的非常详细,对大家的学... 目录一、背景二、环境三、配置nexus上传账号四、执行deploy命令上传包1. 首先需要把本地仓中要

Spring Cloud之注册中心Nacos的使用详解

《SpringCloud之注册中心Nacos的使用详解》本文介绍SpringCloudAlibaba中的Nacos组件,对比了Nacos与Eureka的区别,展示了如何在项目中引入SpringClo... 目录Naacos服务注册/服务发现引⼊Spring Cloud Alibaba依赖引入Naco编程s依

Java springBoot初步使用websocket的代码示例

《JavaspringBoot初步使用websocket的代码示例》:本文主要介绍JavaspringBoot初步使用websocket的相关资料,WebSocket是一种实现实时双向通信的协... 目录一、什么是websocket二、依赖坐标地址1.springBoot父级依赖2.springBoot依赖

Java使用Mail构建邮件功能的完整指南

《Java使用Mail构建邮件功能的完整指南》JavaMailAPI是一个功能强大的工具,它可以帮助开发者轻松实现邮件的发送与接收功能,本文将介绍如何使用JavaMail发送和接收邮件,希望对大家有所... 目录1、简述2、主要特点3、发送样例3.1 发送纯文本邮件3.2 发送 html 邮件3.3 发送带

Win32下C++实现快速获取硬盘分区信息

《Win32下C++实现快速获取硬盘分区信息》这篇文章主要为大家详细介绍了Win32下C++如何实现快速获取硬盘分区信息,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 实现代码CDiskDriveUtils.h#pragma once #include <wtypesbase

使用DeepSeek搭建个人知识库(在笔记本电脑上)

《使用DeepSeek搭建个人知识库(在笔记本电脑上)》本文介绍了如何在笔记本电脑上使用DeepSeek和开源工具搭建个人知识库,通过安装DeepSeek和RAGFlow,并使用CherryStudi... 目录部署环境软件清单安装DeepSeek安装Cherry Studio安装RAGFlow设置知识库总

Python FastAPI入门安装使用

《PythonFastAPI入门安装使用》FastAPI是一个现代、快速的PythonWeb框架,用于构建API,它基于Python3.6+的类型提示特性,使得代码更加简洁且易于绶护,这篇文章主要介... 目录第一节:FastAPI入门一、FastAPI框架介绍什么是ASGI服务(WSGI)二、FastAP

Spring-AOP-ProceedingJoinPoint的使用详解

《Spring-AOP-ProceedingJoinPoint的使用详解》:本文主要介绍Spring-AOP-ProceedingJoinPoint的使用方式,具有很好的参考价值,希望对大家有所帮... 目录ProceedingJoinPoijsnt简介获取环绕通知方法的相关信息1.proceed()2.g

Maven pom.xml文件中build,plugin标签的使用小结

《Mavenpom.xml文件中build,plugin标签的使用小结》本文主要介绍了Mavenpom.xml文件中build,plugin标签的使用小结,文中通过示例代码介绍的非常详细,对大家的学... 目录<build> 标签Plugins插件<build> 标签<build> 标签是 pom.XML