C++学习笔记----9、发现继承的技巧(六)---- 有趣且令人迷惑的继承问题(7)

6、非公共继承

        在前面所有的例子中,父类总是用public关键字列出。你可能会想是否父类也可以是private或protected。实际上,是可以的,但都不像public一样常见。如果不对父类进行访问指示符的指定,对于类来讲就是private继承,对于struct是public继承。

        将与父类的关系声明为protected意味着从基类来的public成员函数与数据成员变为了继承类中的protected。类似地,指定为private继承意味着基类的所有public与protected成员函数与数据成员在继承类中会变成private。

        可能想要统一地对父类的访问层次用这种方式进行降级有几个原因,但大部分原因是层次结构设计的瑕疵。有些程序员滥用这个语言特性,常见于多重继承的组合,应用了类的“部件”。不是使Airplane类包含一个引擎数据成员与一个机身数据成员,反而是使Airplane类是一个protected引擎与一个protected机身。用这种方式,对客户端代码来讲,Airplane既不像引擎,也不像机身(因为所有的东东都是protected),但是可以内部使用所有这些功能。

        注意:如果没有其他原因,只是不熟悉的话,非公共继承很少见,推荐谨慎使用。

7、虚基类

        在本章的前面部分,学到了不明确的基类,当多重父类每个都有一个通用属性的父类时会加剧这种不明确,如下图所示:

        早期的推荐方案是确保共享的父类没有自身的任何功能。那样的话,其成员函数永远不会被调用,就不会有不明确的问题。

        C++有另外的方法,叫做虚基类,来解决这种问题,如果你确实想让共享父类有自身的功能的话。如果共享父类被标记为虑基类,就不会有不明确性。下面的代码添加了一个sleep()成员函数,包含了一个实现,对于Animal基类来说,修改了Dog与Bird类,作为虚基类继承了Animal。在不用虚基类的情况下,在DogBird对象上调用sleep()会是不明确的,因为DogBird有两个Animal的子对象,一个来自于Dog,一个来自于Bird,所以会产生编译器错误。然而,当Animal是虚继承时,DogBird只有一个Animal的子对象,所以调用sleep()时不会有不明确性。

import std;using namespace std;class Animal
{
public:virtual void eat() = 0;virtual void sleep() { println("zzzzz...."); }
};class Dog : public virtual Animal
{
public:virtual void bark() { println("Woof!"); }void eat() override { println("The dog ate."); }
};class Bird : public virtual Animal
{
public:virtual void chirp() { println("Chirp!"); }void eat() override { println("The bird ate."); }
};class DogBird : public Dog, public Bird
{
public:void eat() override { Dog::eat(); }
};int main()
{DogBird myConfusedAnimal;myConfusedAnimal.sleep();  // Not ambiguous because of virtual base class
}

        在这种类层次结构中要注意构造函数。例如,下面的代码添加了一些不同类的数据成员,添加了构造函数来初始化这些数据成员,添加了Animal的缺省构造函数,原因会在代码之后进行解释。

class Animal
{
public:explicit Animal(double weight) : m_weight{ weight } {}virtual double getWeight() const { return m_weight; }
protected:Animal() = default;
private:double m_weight{ 0.0 };
};class Dog : public virtual Animal
{
public:explicit Dog(double weight, string name) : Animal{ weight }, m_name{ move(name) } {}
private:string m_name;
};class Bird : public virtual Animal
{
public:explicit Bird(double weight, bool canFly) : Animal{ weight }, m_canFly{ canFly } {}
private:bool m_canFly{ false };
};class DogBird : public Dog, public Bird
{
public:explicit DogBird(double weight, string name, bool canFly): Dog{ weight, move(name) }, Bird{ weight, canFly } {}
};int main()
{DogBird dogBird{ 22.33, "Bella", true };println("Weight: {}", dogBird.getWeight());
}

        当运行这段代码时,结果并不是预想的那样:

Weight: 0

        看起来给定的22.33的重量在main()函数中的DogBird构造时丢失掉了。怎么回事?这段代码使用了虚Animal基类;因此,DogBird实例只有一个Animal子对象。DogBird构造函数调用了Dog与Bird的构造函数,都指向了Animal基类的构造函数。这就意味着Animal被构造了两次。这是不允许的。在这种情况下,当从继承类的构造函数中调用时,编译器使对Dog与Bird构造函数中的对Animal构造函数的调用失效,取而代之的是调用了Animal基类的缺省构造函数,这样就需要Animal的protected缺省构造函数。所有这些意味着大部分继承类自身要对共享类的构造函数调用负责。正确的实现如下:

class Animal
{
public:explicit Animal(double weight) : m_weight{ weight } {}virtual double getWeight() const { return m_weight; }
protected:Animal() = default;
private:double m_weight{ 0.0 };
};class Dog : public virtual Animal
{
public:explicit Dog(double weight, string name) : Animal{ weight }, m_name{ move(name) } {}
protected:explicit Dog(string name) : m_name{ move(name) } {}
private:string m_name;
};class Bird : public virtual Animal
{
public:explicit Bird(double weight, bool canFly) : Animal{ weight }, m_canFly{ canFly } {}
protected:explicit Bird(bool canFly) : m_canFly{ canFly } {}
private:bool m_canFly{ false };
};class DogBird : public Dog, public Bird
{
public:explicit DogBird(double weight, string name, bool canFly): Animal { weight }, Dog{ move(name) }, Bird{ canFly } {}
};int main()
{DogBird dogBird{ 22.33, "Bella", true };println("Weight: {}", dogBird.getWeight());
}

        在这个实现中,给Dog与Bird添加了protected单参数构造函数。由于只用于继承类所以是protected。客户端代码只用两个参数的构造函数来构造Dog与Bird。

        在做了这些修改后,输出正确:

Weight: 22.33

        注意:在类层次结构中虚基类是避免不明确性的一个伟大的方式。唯一的缺点是许多c++程序员对这个概念还不是很熟悉。

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

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

相关文章

Linux服务控制及系统基本加固

一. liunx操作系统的开机引导的过程 1. 开机自检 根据bios的设置,对cpu,内存,显卡,键盘等等设备进行初步检测如果以上检测设备工作正常,系统会把控制权移交到硬盘 总结:检测出包含系统启动操作系统的设备,硬盘&#…

k8s 处理namespace删除一直处于Terminating —— 筑梦之路

问题现象 k8s集群要清理某个名空间,把该名空间下的资源全部删除后,删除名空间,一直处于Terminating状态,无法完全清理掉。 如何处理 为什么要记录下这个处理的步骤,经过查询资料,网上也有各种各样的方法&…

百货零售行业信息化蓝图整体规划方案|165页PPT

文件《百货零售行业信息化蓝图整体规划方案》是一份针对百货零售行业的信息化转型和供应链低碳解决方案的详细规划报告。以下是对报告的主要内容的分析和总结: 1. 项目概况 议程:包括项目概况、蓝图工作汇报、业务方案概览、详细业务方案、下阶段工作计…

SQL,力扣题目571, 给定数字的频率查询中位数

一、力扣链接 LeetCode_571 二、题目描述 Numbers 表: ------------------- | Column Name | Type | ------------------- | num | int | | frequency | int | ------------------- num 是这张表的主键(具有唯一值的列)。 这张表的每一行表示某个数…

move_base

move_base 官方介绍:http://wiki.ros.org/move_base 如果在仿真环境下, sensor source、odometry source 和 sensor transforms 都已提供好,我们只需要完成以下部分: 一、编写导航程序 ①创建 ROS 工作空间 和 pkg 包 mkdir -p …

标签权重的计算方法之贝叶斯平滑

贝叶斯平滑(Bayesian Smoothing)是一种平滑技术,用于解决评分和标签数据中的样本稀疏性问题。在某些推荐系统或广告点击率预估的场景中,我们可能会遇到标签(例如点击次数和曝光次数)不均衡的问题&#xff0…

QML项目实战:自定义Switch按钮

目录 一.添加模块 1.QtQuick.Controls 2.1 2.QtGraphicalEffects 1.12 二.自定义Switch 三.标签 四.效果 五.代码 一.添加模块 1.QtQuick.Controls 2.1 QtQuick.Controls 提供了一组预定义的 UI 控件,这些控件可以用于构建现代、响应式的用户界面。它包括按…

【勘误笔记】J-LINK连接不上芯片问题

1. 常见的问题 https://mp.weixin.qq.com/s/Ik1czY0jiUkZK21qmeF-yA#rd 2. 记录问题 烧录的固件起始地址为0x08004000 芯片烧录后导致,后面使用J-LINK怎么样都连接不上, 当时硬件是BOOT 0接地应该是上电进入flash代码有问题 考虑到启动模式 把BOOT 0不…

职场高手揭秘,细节如何左右你的成败与升迁之路

身在职场,每一个人都想得到老板的器重,能不断地加薪、升职,从而获得职场的成功。但你知道,影响一个人职场成功,或者说影响升职加薪的最重要因素是什么吗? 许多人会说那要靠运气,也有人认为工作…

SSM大学生校园交流论坛-计算机设计毕业源码31910

摘 要 随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟。本文介绍了大学生校园交流论坛的开发全过程。通过分析大学生校园交流论坛管理的不足,创建了一个计算机管理大学生校园交流论坛的方案。文章介绍了大学生校园交…

c语言简单编程练习9

1、输入一个整数&#xff0c;判断它是否为回文数 例&#xff1a;输入&#xff1a;12321 输出&#xff1a;该数是回文数 输入&#xff1a;12323 输出&#xff1a;该数不是回文数 #include <stdio.h> int huiwenshu(int num) {int a[100];int i, n, j…

技术面没过,竟然是因为我没用过Pytest框架?

01 概述 pytest是一个非常成熟的全功能的Python测试框架&#xff0c;主要特点有以下几点&#xff1a; 简单灵活&#xff0c;容易上手&#xff0c;文档丰富&#xff1b; 支持参数化&#xff0c;可以细粒度地控制要测试的测试用例&#xff1b; 能够支持简单的单元测试和复杂的…

通用方式创建未知文件后缀文件

困惑&#xff1a;比如平时想创一个类似&#xff1a;Dockerfile 文件如何玩&#xff1f; entrypoint.sh 如何玩&#xff1f; windows平台&#xff0c;直接命令行&#xff1a; mac平台或者linux平台也类似

如何用PPT画箭头?用这2个ppt软件快速完成绘图!

ppt怎么画箭头&#xff1f; 有时在ppt中绘制流程图或传达承上启下的含义时&#xff0c;会用到箭头形状&#xff0c;运用到箭头元素来增强表达的清晰度和逻辑性。那可能有人会问&#xff0c;ppt怎么画箭头&#xff1f; 这似乎是一个小问题&#xff0c;但如果你对ppt工具不够熟…

自攻螺钉的世纪演变:探索关键设计与应用

自攻螺钉作为现代工业和建筑中的不可或缺的标准部件&#xff0c;经过了超过100年的发展和创新。从1914年最早的铁螺钉设计到今天的自钻自攻螺钉&#xff0c;自攻螺钉的设计不断优化&#xff0c;以适应更复杂的应用需求。本文将回顾自攻螺钉的演变历程&#xff0c;分析其设计原理…

HTB:PermX[WriteUP]

目录 连接至HTB服务器并启动靶机 1.How many TCP ports are listening on PermX? 使用nmap对靶机TCP端口进行开放扫描 2.What is the default domain name used by the web server on the box? 使用curl访问靶机80端口 3.On what subdomain of permx.htb is there an o…

Python 项目国际化:使用 Babel 实现多语言支持

文章目录 如何使用 Babel 实现 Python 项目国际化1. 安装 Babel2. 设置项目目录结构3. 标记可翻译的文本4. 提取可翻译的文本生成文件 —— 生成pot文件4.1 有配置文件方式&#xff08;使用 babel.cfg&#xff09;4.1.1. 创建 babel.cfg 文件4.1.2. 提取翻译内容 4.2 无配置文件…

信号-2-信号捕捉

相关概念&#xff1a;递达 未决 / 阻塞 忽略 阻塞 vs 忽略 阻塞&#xff1a; 如果指定信号信号被阻塞&#xff0c; block期间该信号不能被递达&#xff0c;一直在pending表中。知道block被撤销后&#xff0c; 该信号才能递达&#xff0c;递达后对应pending位置置零。 忽…

正则表达式1 re.match惰性匹配详解案例

点个关注 re.match() re.match() 函数尝试从字符串的开头开始匹配一个模式&#xff0c;如果匹配成功&#xff0c;返回一个匹配成功的对象&#xff0c;否则返回None。大小写区分&#xff0c;内容匹配不到后面的,只能匹配一个&#xff0c;不能有空格&#xff08;开头匹配&#…

如何针对云计算安全进行等保测评?

等级保护作为我国网络安全法明确的重要制度&#xff0c;已在我国信息系统安全保驾护航中发挥着重要作用。目前&#xff0c;等级保护已经进入了2.0时代&#xff0c;“云、大、物、移、工控”纳入等保监管。 当前&#xff0c;按照传统等级保护技术要求实施的安全策略已经不能适应…