C++多继承,虚继承部分总结与示例

news/2024/4/24 14:24:45/文章来源:https://blog.csdn.net/qq_41437512/article/details/129202412

tags: C++ OOP

写在前面

写一下多继承, 虚继承的一些部分, 包括一些例子.

多继承

简介

多继承是指从多个直接基类中产生派生类的能力. 多继承的派生类继承了所有父类的属性, 所以会带来一些复杂的问题.

示例1: 多继承用法与调用顺序

#include <string>
#include <iostream>
using namespace std;class ZooAnimal {
public:ZooAnimal() { cout << "call ZooAnimal::ZooAnimal()\n"; }~ZooAnimal() { cout << "call ZooAnimal::~ZooAnimal()\n"; }
};class Endangered {
public:Endangered() { cout << "call Endangered::Endangered()\n"; }Endangered(int a) : m_a(a) { cout << "call Endangered::Endangered(int)\n"; }~Endangered() { cout << "call Endangered::~Endangered()\n"; }static int critical;private:int m_a;
};
int Endangered::critical = 10;class Bear : public ZooAnimal {
public:Bear() { cout << "call Bear::Bear()\n"; }Bear(string, bool, string);~Bear() { cout << "call Bear::~Bear()\n"; }
};
Bear::Bear(string name, bool onExhibit, string detail) {cout << "call Bear::Bear(string, bool, string)\n";
}// multi inherit
class Panda : public Bear, public Endangered {
public:Panda();Panda(string, bool);~Panda() { cout << "call Panda::~Panda()\n"; }
};Panda::Panda(string name, bool onExhibit): Bear(name, onExhibit, "Panda"), Endangered(Endangered::critical) {cout << "call Panda::Panda(string, bool)\n";
}
Panda::Panda() : Endangered(Endangered::critical) {cout << "call Panda::Panda()\n";
}void t1() {//Panda p1;/* call ZooAnimal::ZooAnimal() *//* call Bear::Bear() *//* call Endangered::Endangered(int) *//* call Panda::Panda() *//* call Panda::~Panda() *//* call Endangered::~Endangered() *//* call Bear::~Bear() *//* call ZooAnimal::~ZooAnimal() */
}int main(int argc, char *argv[]) {t1();return 0;
}

通过输出可以看出, 首先调用最终基类, 然后调用直接基类, 然后是第二基类, 最后是子类.

析构顺序正好相反(由子类至基类).

示例2: 多继承构造函数可能出现的问题

#include <iostream>
#include <memory>
#include <string>
using namespace std;struct Base1 {Base1() { cout << "Base1()\n"; }Base1(const string &) { cout << "Base1(const string&) \n"; }Base1(std::shared_ptr<int>);
};struct Base2 {Base2() { cout << "Base2()\n"; }Base2(const string &) { cout << "Base2(const string&) \n"; }Base2(int a) { cout << "Base2(int)\n"; }
};struct D1 : public Base1, public Base2 {using Base1::Base1;using Base2::Base2;// 定义自己版本的构造函数D1(const string &) { cout << "D1(const string &)\n"; }
};void t1() {D1 d1(1);/* Base1() *//* Base2(int) */
}
void t2() {D1 d2("abc");/* error: call of overloaded 'D1(const char [4])' is ambiguous *//* Base1() *//* Base2() *//* D1(const string &) */
}int main(int argc, char *argv[]) {// test/* t1(); */t2();return 0;
}

多继承引发的类型转换问题

在只有一个基类的情况下, 派生类的指针或引用能自动转换成一个可访问基类的指针或引用, 多基类情况类似, 可以令某个可访问基类的指针或引用直接指向一个派生类对象. 如下所示:(仍然采用上面的Panda例子)

void print(const Bear &) { cout << "call print(const Bear&)\n"; }
void highlight(const Endangered &) {cout << "call highlight(const Endangered&)\n";
}
ostream &operator<<(ostream &os, const ZooAnimal &) {os << "call operator<< (ZooAnimal)\n";return os;
}
// 如果解注释, 会导致错误
/* error: call of overloaded 'print(Panda&)' is ambiguous */
/* void print(const Endangered &) { cout << "call print(const Endangered&)\n"; } */
void t2() {Panda aa("aa", true);print(aa);highlight(aa);cout << aa << endl;/* call print(const Bear&) *//* call highlight(const Endangered&) *//* call operator<< (ZooAnimal) */
}

上面如果为同一函数的参数作两个基类的重载, 那么就会导致二义性错误.

基于指针类型或引用类型的查找

与只有一个基类的继承一样, 对象/指针/引用的静态类型决定了能使用的成员.

例如: 如果使用一个ZooAnimal指针, 则只有定义在ZooAnimal中的操作可以调用, 而Pandas中的其他特有部分(其他基类, Bear,Panda,Endangered)都不可见.

class ZooAnimal {
public:ZooAnimal() { cout << "call ZooAnimal::ZooAnimal()\n"; }void print() { cout << "call ZooAnimal::print()\n"; }~ZooAnimal() { cout << "call ZooAnimal::~ZooAnimal()\n"; }
};void t3() {Bear *pb = new Panda("aa", true);pb->print();delete pb;/* call ZooAnimal::ZooAnimal() *//* call Bear::Bear(string, bool, string) *//* call Endangered::Endangered(int) *//* call Panda::Panda(string, bool) *//* call ZooAnimal::print() *//* call Bear::~Bear() *//* call ZooAnimal::~ZooAnimal() */
}

多继承下的类作用域

在只有一个基类的情况下, 派生类的作用域嵌套在直接基类和间接基类的作用域中. 查找过程沿着继承体系自底向上进行, 直到找到所需的名字. 派生类的名字将隐藏基类的同名成员.

多继承中, 相同的查找过程在所有直接基类中同时进行, 若同一名字在多个基类中找到, 则二义性错误.

所以, 需要显式指明作用域, 或者定义新版本的不同名内容.

虚继承

派生列表中同一基类只能出现一次, 但是实际上派生类可以多次继承同一个类, 派生类可以通过它的两个直接基类分别继承同一个间接基类, 也可以直接继承某个基类, 然后通过另一个基类再一次间接继承该类.

默认情况下, 派生类中含有继承链上的每一个类对应的子部分, 如果某个类在派生过程中出现多次, 则派生类中将包含该类的多个子对象.

虚继承的出现就是用于解决多继承中存在的基类多次使用问题的.

其目的是: 令某一个类作出声明, 承诺愿意共享基类, 其中, 共享的基类子对象称为虚基类.

在这种机制下, 不论虚基类在继承体系中出现了多少次, 在派生类中都只包含唯一一个共享的虚基类子对象.

  • 必须在虚派生的真实需求出现之前完成派生操作.
  • 虚派生只会影响从指定了虚基类的派生类中进一步派生出的类, 不会影响派生类本身.
  • 使用virtual说明符(在public之前或者之后都可以)表明了: 在后续的派生类中共享虚基类的同一份实例, 但是并没有规定什么样的类能够作为虚基类.

示例: Panda

#include <string>
#include <iostream>
using namespace std;class ZooAnimal {
public:ZooAnimal() {}void print() { cout << "call ZooAnimal::print()\n"; }~ZooAnimal() {}
};class Endangered {
public:Endangered() {}Endangered(int a) : m_a(a) {}~Endangered() {}static int critical;private:int m_a;
};
int Endangered::critical = 10;class Raccoon : virtual public ZooAnimal {
public:Raccoon() {}Raccoon(string, bool, string);~Raccoon() {}
};
Raccoon::Raccoon(string name, bool onExhibit, string detail) {}class Bear : virtual public ZooAnimal {
public:Bear() {}Bear(string, bool, string);~Bear() {}
};
Bear::Bear(string name, bool onExhibit, string detail) {}// multi inherit
class Panda : public Bear, public Raccoon, public Endangered {
public:Panda();Panda(string, bool);~Panda() {}
};Panda::Panda(string name, bool onExhibit): Bear(name, onExhibit, "Panda"), Endangered(Endangered::critical) {}
Panda::Panda() : Endangered(Endangered::critical) {}void t1() {Panda a;/* call ZooAnimal::ZooAnimal() *//* call Bear::Bear() *//* call Raccoon::Raccoon() *//* call Endangered::Endangered(int) *//* call Panda::Panda() *//* call Panda::~Panda() *//* call Endangered::~Endangered() *//* call Raccoon::~Raccoon() *//* call Bear::~Bear() *//* call ZooAnimal::~ZooAnimal() */
}

虚派生中, 虚基类是由最底层的派生类初始化的, 以上面的程序为例, 当创建Panda对象时, 由Panda构造函数独自控制ZooAnimal的初始化过程.

支持向基类的常规类型转换

与非虚基类一样, 派生类对象也可以被可访问基类的指针或引用操作.

void dance(const Bear &) { cout << "call dance(const Bear&)\n"; }
void rummage(const Raccoon &) { cout << "call rummage(const Raccoon&)\n"; }
ostream &operator<<(ostream &os, const ZooAnimal &) {os << "call operator<< (ZooAnimal)\n";return os;
}void t2() {Panda a;dance(a);rummage(a);cout << a;/* call dance(const Bear&) *//* call rummage(const Raccoon&) *//* call operator<< (ZooAnimal) */
}

虚基类成员的可见性

因为在每一个共享的虚基类中只有唯一一个共享的子对象, 所以该基类的成员可以被直接访问, 并且不会产生二义性.

如果虚基类的成员只被一条派生路径覆盖, 则我们仍可以直接访问这个被覆盖的成员, 但是如果成员被多于一个的基类覆盖(例子: 菱形继承), 派生类就必须为该成员自定义一个新版本.

虚继承对象的构造方式

  • 首先使用提供给最底层派生类构造函数的初始值初始化该对象的虚基类子部分;
  • 接下来按照直接基类在派生列表中出现的次序依次对其初始化.

例子中的顺序:

  1. 使用Panda的构造函数初始值列表中提供的初始值构造虚基类ZooAnimal部分
  2. 构造Bear
  3. 构造Raccoon
  4. 构造Endangered
  5. 构造Panda

如果Panda没有显式初始化ZooAnimal基类, 则调用ZooAnimal的默认构造函数, 所以此时一定要有默认构造函数.

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

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

相关文章

ChatGPT爆火:AI崛起,这些职场人的机遇到了?

ChatGPT最近真的被全球吃瓜群众玩坏了&#xff01; 回答情感问题&#xff0c;编写代码&#xff0c;撰写slogan或脚本&#xff0c;甚至还被用于毕业生论文…… 这个连马斯克都由衷地称赞的ChatGPT&#xff0c;是一种全新的聊天机器人模型。上线2个月&#xff0c;就拥有了上亿活…

04--WXML

1、什么是WXML什么是Wxml呢&#xff1f;我们首先要介绍一下Html&#xff0c;Html的全称为HyperTextMarkup Language&#xff0c;翻译过来就是超文本标记语言&#xff0c;这种语言目前已经普遍用于前端开发&#xff0c;而wxml正是从html演变而来&#xff0c;它基于微信这个平台&…

SQL server设置用户只能访问特定数据库、访问特定表或视图

在实际业务场景我们可能需要开放单独用户给第三方使用&#xff0c;并且不想让第三方看到与业务不相关的表或视图&#xff0c;我们需要在数据库中设置一切权限来实现此功能&#xff1a; 1.设置用户只能查看数据库中特定的视图或表 1.创建用户名 选择默认数据库 服务器角色默认…

__stack_chk_fail问题分析

一、问题进程收到SIGABRT信号异常退出&#xff0c;异常调用栈显示__stack_chk_fail*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** Build fingerprint: Pico/A7H10/PICOA7H10:10/5.5.0/smartcm.1676912090:userdebug/dev-keys Revision: 0 ABI: arm64 Times…

Web前端学习:一

编辑器的基础使用 编辑器推荐使用&#xff1a; HBuilderx&#xff08;免费中文&#xff09;&#xff08;建议使用&#xff09; Sublime&#xff08;免费英文&#xff09; Sublime中文设置方法&#xff0c;下载语言插件&#xff1a; 1、进入Sublime后&#xff0c;ShiftCtrlP…

wait/notify方法 等待唤醒机制

线程正在运行&#xff0c;调用这个线程的wait()方法&#xff0c;这个线程就会进入一个集合进行等待(这个集合的线程不会争抢cpu)&#xff0c;此时线程的状态就是waiting 当有线程调用notify()方法的时候&#xff0c;就会从集合中挑选一个线程进入到排队队列里面 notifyAll就是…

【样式】轮播图样式 uview 版本 : “2.0.31“

![在这里插入图片描述](https://img-blog.csdnimg.cn/6cd568ce932b4ea7ae52f10365979680.png html <view class"addSwiperdiv"><image src"/static/66.png" mode"aspectFill" class"titleimg"></image><view c…

VC++ 解决dll库动态库加载失败问题(调用LoadLibrary加载失败)(附源码)

目录 1、动态加载dll库去调用库中的函数 1.1、调用系统dll库中未公开的接口 1.2、调用控件库中的注册接口向系统中注册该控件 2、LoadLibrary动态加载dll库失败的场景 2.1、自制安装包中遇到的LoadLibrary加载dll库失败问题 2.2、主程序底层模块调用LoadLibrary加载dll库…

秒杀测试案例 Java Redis Mysql

基于redis和MySQL乐观锁实现秒杀优惠券场景&#xff0c;一人一单。MySQL乐观锁改良控制不出现超卖和少卖问题&#xff0c;使用redisson分布式锁在用户维度加锁控制一人一单。 源码&#xff1a;https://github.com/hanhanhanxu/SeckillTest 文中图片看不清的地方可以鼠标右键-&…

谷歌外推留痕,谷歌搜索留痕快速收录怎么做出来的?

本文主要分享谷歌搜索留痕的收录效果是怎么做的&#xff0c;让你对谷歌留痕技术有一个全面的了解。 本文由光算创作&#xff0c;有可能会被修改和剽窃&#xff0c;我们佛系对待这样的行为吧。 谷歌搜索留痕快速收录怎么做出来的&#xff1f; 答案是&#xff1a;通过谷歌蜘蛛…

C语言-结构体对齐

详细说明参考博客 (1条消息) C语言结构体对齐&#xff0c;超详细&#xff0c;超易懂_haozigegie的博客-CSDN博客 (1条消息) #pragma pack详解_OuJiang2021的博客-CSDN博客_#pragma pack 以下个人理解总结 出现结构体对齐考虑的根本原因就是&#xff1a;【数据存取执行效率】…

Openwrt中动态IPV6 防火墙的正确设置方法

环境&#xff1a;光猫桥接公网IPV6 问题&#xff1a;动态IPV6地址不知道怎么设置防火墙 解决办法&#xff1a;模糊匹配前缀&#xff0c;特定后缀 背景&#xff1a;将家中光猫桥接后&#xff0c;获得了公网的IPV6地址&#xff0c;可以从外部用IPV6访问家中的设备&#xff0c;但I…

【AI写作】 机器人流程自动化 介绍 - Robotic Process Automation (RPA) Introduction

写一篇文章介绍RPA技术,未来的发展。使用markdown格式,有3级索引,超过3000字。 某位大师说过的: 任何行业、任何部门都有大量的场景,涉及重复、有规则逻辑的工作,都可以用 RPA 开发一个软件机器人帮助完成。 文章目录 机器人过程自动化(RPA)简介RPA的定义RPA的好处Robo…

【centos7下部署mongodb】

一.安装环境 CentOS7MongoDB4.0.13正式版。 二.下载MongoDB 1.1 官网下载地址&#xff1a;https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-4.0.13.tgz 1.2 将压缩包通过xftp上传到服务器/opt目录&#xff0c;然后解压、改名 三. 配置环境变量及配置文件 3.1配置系…

有限差分法求解不可压NS方程

网上关于有限差分法解NS方程的程序实现不尽完备&#xff0c;这里是一些补充注解 现有的优秀资料 理论向 【1】如何从物理意义上理解NS方程&#xff1f; - 知乎 【2】NS方程数值解法&#xff1a;投影法的简单应用 - 知乎 【3】[计算流体力学] NS 方程的速度压力法差分格式_…

pytorch1.2.0+python3.6

一、说明 pytorch1.2.0python3.6CUDA10.0cudnn7.4.1.5 二、步骤 在conda中创建一个新的虚拟环境 查看一下自己的所有环境 激活虚拟环境 conda activate torch1.2.0 关于cuda和cudnn 1、查看自己电脑系统是10.2版本 http://链接&#xff1a;https://pan.baidu.com/s/1v5cN6…

自学前端,你必须要掌握的3种定时任务

当你看到这篇博客的时候&#xff0c;一定会和狗哥结下不解之缘&#xff0c;因为狗哥的博客里不仅仅有代码&#xff0c;还有很多代码之外的东西&#xff0c;如果你可以看到最底部&#xff0c;看到投票环节&#xff0c;我相信你一定感觉到了&#xff0c;狗哥的真诚&#xff0c;狗…

DateTimeParseException

前端请求为字符串的时间格式2023-02-16 19:19:51&#xff0c;服务端用LocalDateTime类型接收时报解析异常java.time.format.DateTimeParseException: Text 2023-02-16 19:19:51 could not be parsed at index 10方法一&#xff1a;JsonFormat(shape Shape.STRING, pattern &q…

Redis 主从复制-服务器搭建【薪火相传/哨兵模式】

Redis 安装参考文章&#xff1a;Centos7 安装并启动 Redis-6.2.6 注意&#xff1a;本篇文章操作&#xff0c;不能在 静态IP地址 下操作&#xff0c;必须是 动态IP地址&#xff0c;否则最后主从服务器配置不成功&#xff01; 管道符查看所有redis进程&#xff1a;ps -ef|grep re…

Linux->父子进程初识和进程状态

目录 前言&#xff1a; 1. 父子进程创建 2. 进程状态 R(running)状态&#xff1a; S(sleep)状态&#xff1a; D(disk sleep)状态&#xff1a; T(stopped)状态&#xff1a; X(dead)和Z(zombie)状态&#xff1a; 孤儿进程&#xff1a; 前言&#xff1a; 本篇主要讲解关…