多重继承与虚继承

资料\经典C++pdf\参考文献 资料\经典C++pdf\参考文献很好哟,千万记得下载,里边讲得很详细,比喻生动恰当,逻辑性强,透彻,清晰,易懂 立即下载

多重继承
 
   在多重继承中,基类的构造函数的调用次序既不受派生类构造函数初始化列表中出现的基类构造函数的影响,也不受基类在构造函数初始化列表中的出现次序的影响,它按照基类在类派生列表中的出现次序依次调用相应的基类构造函数。析构顺序与构造顺序逆序进行。
 
   多重继承中,派生类的指针或引用可以转换为其任意基类的指针或引用。因此,这种转换更可能遇到二义性问题。
 
   在多重继承中,成员函数中使用的名字的查找首先在函数本身进行,如果不能在本地找到名字,就继续在成员的类中查找,然后同时(并行)查找所有基类继承子树。多重继承的派生类有可能从两个或多个基类继承同名成员,对该成员不加限定的使用是二义性的。
 
   注意,多重继承中首先发生名字查找。你可能会感到吃惊的是,即使两个继承的同名函数有不同的形参表,也会产生错误。类似地,即使函数在一个类中是私有的而在另一个类中是公有或者受保护的,也是错误的。或者,在一个类给定义了函数,而在另一个类中没有定义,调用仍是错误的。例如:
 

#include <iostream>

class Base1 {
public:
    void print() {}
    void display() {}
    void show() {}
};

class Base2 {
public:
    void print(int) {}
    void show(int);
private:
    void display(int) {}
};

class Derived: public Base1, public Base2 {
};

int main () {
    Derived d;
    d.print();
    d.display();
    d.show();
    
    return 0;
}

编译结果(MinGW2.05):
Compiling source file(s)...
main.cpp
main.cpp: In function `int main()':
main.cpp:24: error: request for member `print' is ambiguous
main.cpp:13: error: candidates are: void Base2::print(int)
main.cpp:6: error:                 void Base1::print()
main.cpp:25: error: request for member `display' is ambiguous
main.cpp:16: error: candidates are: void Base2::display(int)
main.cpp:7: error:                 void Base1::display()
main.cpp:26: error: request for member `show' is ambiguous
main.cpp:14: error: candidates are: void Base2::show(int)
main.cpp:8: error:                 void Base1::show()

Test.exe - 9 error(s), 0 warning(s)



 
   解决这种二义性的方法可以是通过指定使用哪个类的版本(即带上类名前缀)来解决。但最好的方法是,在解决二义性的派生类中定义函数的一个版本。
 
虚继承
 
   在标准I/O库中的类都继承了一个共同的抽象基类ios,那个抽象基类管理流的条件状态并保存流所读写的缓冲区。istream和ostream类直接继承这个公共基类,库定义了另一个名为isotream的类,它同时继承istream和ostream,iostream类既可以对流进行读又可以对流进行写。如果I/O类型使用常规继承,则每个iostream对象可能包含两个ios子对象:一个包含在它的istream子对象中,另一个包含在它的ostream子对象中。从设计角度讲,这个实现是错误的:iostream类想要对单个缓冲区进行读和写,它希望跨越输入和输出操作符共享条件状态。
 
   在C++中,通过使用 虚继承(virtual inheritance)解决这类问题。虚继承是一种机制,类通过虚继承指出它希望共享其虚基类的状态。在虚继承下,对给定虚基类,无论该类在派生层次中作为虚基类出现多少次,只继承一个共享的基类子对象。共享的基类子对象称为 虚基类(virtual base class)。
 
   通过在派生类列表中包含关键字virtual设置虚基类,例如:
   class istream : public virtual ios {...};
   class ostream : virtual public ios {...};
   class iostream : public istream, public ostream {...};
 
   假定通过多个派生路径继承名为X的成员,有下面三种可能性:
   1)如果在每个路径中X表示同一虚基类成员,则没有二义性,因为共享该成员的单个实例;
   2)如果在某个路径中X是虚基类的成员,而在另一路径中X是后代派生类的成员,也没有二义性——特定派生类实例的优先级高于共享虚基类实例。
   3)如果沿每个继承路径X表示后代派生类的不同成员,则该成员的直接访问是二义性的。
 
   例如:
#include <iostream>

class B {
public:
    void print() {
        std::cout << "B" << std::endl;
    }
};

class D1: public virtual B {
};

class D2: public virtual B {
public:
    void print() {
        std::cout << "D2" << std::endl;
    }
};

class DD: public D1, public D2 {
};

int main () {
    DD d;
    d.print();    // ok: call D2::print
    
    return 0;
}


 
特殊的初始化语义
 
   通常,每个类只初始化自己的直接基类。在应用于虚基类的时候,这个初始化策略会失败。如果使用常规规则,就可能会多次初始化虚基类。类将沿着包含该虚基类的每个继承路径初始化。
 
   为了解决这个重复初始化问题,从具有虚基类的类继承的类对初始化进行特殊处理。在虚派生中,由最低层派生类的构造函数初始化虚基类。
 
   虽然由最低层派生类初始化虚基类,但是任何直接或间接继承虚基类的类一般也必须为该基类提供自己的初始化式。只要可以创建虚基类派生类类型的独立对象,该类就必须初始化自己的虚基类,这些初始化式只在创建中间类型的对象时使用。
 
   例如,我们有四个类:ZooAnimal, Bear, Raccoon和Panda,它们之间构造一个继承层次:Bear和Raccoon继承ZooAnimal,Panda继承Bear和Raccoon。那么,它们的构造函数就形如:
   Bear::Bear(std::string name, bool onExhibit): ZooAnimal(name, onExhibit, "Bear") {}
   Raccoon::Raccoon(std::string name, bool onExhibit): ZooAnimal(name, onExhibit, "Raccoon") {}
   Panda::Panda(std::string name, bool onExhibit):  ZooAnimal(name, onExhibit, "Panda"), Bear(name, onExhibit), Raccoon(name, onExhibit) {}
 
   当创建Panda对象的时候,构造过程如下:
   1)首先使用构造函数初始化列表中指定的初始化式构造ZooAnimal部分;
   2)接下来,构造Bear部分(在派生列表中Bear在前)。忽略Bear初始化列表中用于ZooAnimal构造函数的初始化式;
   3)然后,构造Raccoon部分,再次忽略ZooAnimal初始化式;
   4)最后,构造Panda部分。
 
   如果Panda构造函数不显式初始化ZooAnimal基类,就使用ZooAnimal默认构造函数;如果ZooAnimal没有默认构造函数,则代码出错。
 
   无论虚基类出现在继承层次中的任何地方,总是在构造非虚基类之前构造虚基类。
 
   例如,有下面的继承关系:
   class Character { };
   class BookCharater: public Character { };
   class ToyAnimal { };
   class TeddyBear: public BookCharacter, public Bear, public virtual ToyAnimal { };
 
直观继承图为:

   按声明次序检查直接基类,确定是否存在虚基类。上例中,首先检查BookCharacter的继承子树,然后检查Bear的继承子树,最后检查ToyAnimal的继承子树。按从根类开始向下到最低层派生类的次序检查每个子树。在这里依次检查到ZooAnimal和ToyAnimal为虚基类。
 
   TeddyBear的虚基类的构造次序是先ZooAnimal再ToyAnimal(检查到的顺序)。一旦构造了虚基类,就按声明次序调用非虚基类的构造函数:首先是BookCharacter,它导致调用Character构造函数,然后是Bear。在这里,由最低层派生类TeddyBear指定用于ZooAnimal和ToyAnimal的初始化式。
 
   当然,对于析构函数的调用顺序与构造函数相反。
 
   示例代码如下:
#include <iostream>

class Character {
public:
    Character() {
        std::cout << "Character Constructor" << std::endl;
    }
    ~Character() {
        std::cout << "Character Destructor" << std::endl;
    }
};

class BookCharacter: public Character {
public:
    BookCharacter() {
        std::cout << "BookCharacter Constructor" << std::endl;
    }
    ~BookCharacter() {
        std::cout << "BookCharacter Destructor" << std::endl;
    }
};

class ZooAnimal {
public:
    ZooAnimal() {
        std::cout << "ZooAnimal Constructor" << std::endl;
    }
    ~ZooAnimal() {
        std::cout << "ZooAnimal Destructor" << std::endl;
    }
};

class Bear: public virtual ZooAnimal {
public:
    Bear() {
        std::cout << "Bear Constructor" << std::endl;
    }
    ~Bear() {
        std::cout << "Bear Destructor" << std::endl;
    }
};

class ToyAnimal {
public:
    ToyAnimal() {
        std::cout << "ToyAnimal Constructor" << std::endl;
    }
    ~ToyAnimal() {
        std::cout << "ToyAnimal Destructor" << std::endl;
    }
};

class TeddyBear: public BookCharacter, public Bear, public virtual ToyAnimal {
public:
    TeddyBear() {
        std::cout << "TeddyBear Constructor" << std::endl;
    }
    ~TeddyBear() {
        std::cout << "TeddyBear Destructor" << std::endl;
    }
};

int main () {
    TeddyBear tb;
    
    return 0;
}

运行结果如下:
ZooAnimal Constructor
ToyAnimal Constructor
Character Constructor
BookCharacter Constructor
Bear Constructor
TeddyBear Constructor
TeddyBear Destructor
Bear Destructor
BookCharacter Destructor
Character Destructor
ToyAnimal Destructor
ZooAnimal Destructor

Terminated with return code 0
Press any key to continue ...


 

C# 操作Excel的时候,出现"被呼叫方拒绝接受呼叫的异常"的解决方案(参考官方给出的解决方案) 首先官方有对这个错误给出解释和解决方案,连接如下: https://docs.microsoft.com/zh-cn/previous-versions/ms228772(v=vs.120)#%E5%8F%AF%E9%9D%A0%E7%BC%96%E7%A8%8B 简单来说,貌似是线程问题,我猜。。。 之前我尝试着在代码中添加 Thread.Sleep(1000); 来进行解决,这种办... 阅读详情

相关推荐

物理与工程中的‘黑话’:Kronecker delta和Levi-Civita符号到底简化了什么?

本文深入解析了物理与工程中常用的Kronecker delta和Levi-Civita符号的简化作用。通过对比传统表示与张量表示,展示了这些符号在向量运算、矩阵迹计算及流体力学模拟中的高效应用,显著提升计算效率和代码可读性。特别适合理论物理和工程数学领域的学习者与从业者。

weixin_27215403的博客 397

多继承的二义性问题

多继承的二义性问题 在同名隐藏示例中,如果我们去掉派生类中print函数的定义,仍然在主函数中通过对象调用print函数呢? 显然,调用后程序会报错:request for member ‘print’ is ambiguous。译为:成员“print”的请求不明确。这里出现了程序的二义性问题,因为派生类多继承于两个基类,两个基类中都有同名函数print,所以编译出现错误。 下面我们重点看这个图...

qq_42932834的博客 791

ego-planner启动参数深度解析:从配置到实战避障应用

本文深入解析EGO-Planner启动参数的配置与实战应用,重点探讨无人机自主导航中的避障性能优化。通过环境感知、运动约束和算法控制三类参数的详细说明,结合ROS和RViz工具,提供从基础配置到高级调优的完整指南,帮助开发者提升无人机在复杂场景下的规划效率和安全性。

rum55的博客 246

C++虚拟继承注意(成功解决[Error] request for is ambiguous)

C++虚拟继承注意(成功解决继承中的[Error] request for is ambiguous)

m0_66977257的博客 2066

Topas——基于Geant4的放射治疗蒙特卡罗算法模拟工具

本文内容参考Joseph Perl 的线上会议,视频详情可见于,该视频会议内容主要介绍Topas功能、软件架构、工作流程、参数设定、物理模型、具体案例等,对TOPAS的基本入门使用方法以及使用规定作出阐述。

fy方的博客 7602

ET篇:Google.Protobuf的学习(理解基本的使用流程)

据我目前所知,ET使用的数据交换协议是Google.Protobuf,所以今天就来学习一下,但是网上许多人都是以高下立判的方式讲解的,对新手小白很不友好,所以我今天就以纯小白的视角和大家一起学习 学习环境: .NET Core2.2 .NET Framework 4.7.2 Rider protobuf-csharp-3.7.0-rc-2 Google.Protobuf简介 prot...

设计师养成记 3563

c++的继承,虚继承,虚函数,继承权限,多态,析构结合案例讲解

c++的继承,虚继承,虚函数,继承权限,多态,析构结合案例讲解 一切的提高来自于发现问题,内容比较多,希望耐心读完,读完后会对上述的原理有深入理解。 问题的发现 最近在学习QT的时候发现了一个问题,QT的模块对象的事件分发是由event()函数完成,而窗体模块都继承自QObject类,在QObject类中,event()成员是virtual修饰的虚函数,并且函数权限是public。而派生类的eve...

mitugaoyang1993的博客 960

Python 实战:高效绕过 reCAPTCHA v3 的自动化策略

本文深入探讨了使用Python高效绕过reCAPTCHA v3验证的实战策略。文章首先解析了reCAPTCHA v3无感验证的评分机制,随后详细介绍了两种核心解决方案:一是利用CapSolver等专业API服务直接获取有效token,二是通过Playwright模拟真人浏览器行为以提升评分。文中提供了从定位关键参数到代码实现的完整流程,并强调了合规操作与请求频率控制的重要性,为自动化开发者提供了切实可行的技术指南。

weixin_29057065的博客 488

c++ primer plus学习笔记(7)——类继承

2.1多继承 在这一节,我们将围绕构造函数的发生、方法的二义性进行讨论 2.1.1构造、析构顺序:栈原则 假如一个test类继承两个基类:base1、base2,下述代码显示了他们的构造顺序。 #include <iostream> using namespace std; class Base1 { public: Base1() { cout << "Base1 constructor\n"; }; ~Base1() { cout << ".

qq_57404161的博客 324

八大排序算法——(万字图文详解)

本篇博客将对“直接插入排序”、“希尔排序”、“直接选择排序”、“堆排序”、“冒泡排序”、“快速排序”、“归并排序”、“计数排序”进行详细的解析(算法逻辑、具体实现过程、时间复杂度、稳定性),并附以图片帮助大家更好地理解算法逻辑以及实现每个排序的参考代码。

Forward♞的博客 8075

若依ruoyi框架实现单点登录或者接入统一认证

log.info("单点登录用户[{}]不存在, 需要创建.", loginName);log.info("单点登录用户[{}]已存在.", loginName);jsonObjectData.put("nickName","单点1");if (code.equals("0")) {//验证成功需要自动登录。jsonObject.put("msg","验证成功");ajax.put("msg", "登录成功");

GitHub质检员 2万+

【C++】多重继承与虚继承

虚继承可以解决二义性和A被构建多次这两个问题,普通继承不能解决;虚继承通过增加一个指针(浪费了一点存储空间),换取了更高的效率只要一个类虚继承了其它类,那么该类所有的对象中都会新增一个指针,该指针专门用来指向系统中虚基类表的首地址。

qq_62917398的博客 1252

2025华为od机试真题B卷【字符串计数匹配】Java&Go 实现

给你一个字符串str和整数k,返回满足以下条件的所有子字符串个数: 1.恰好包含k个字母。 2.数字0-9各出现至少一次。

MISAYAONE的博客 639

C++中的多重继承与虚继承

先记一下,C++中的关键字,virtual,直译为虚拟的,非真实的。上文讲过多继承,实际情况会更复杂,如果有以下状态,也就是A同时是B和C的父类,然后B和C又同时是D的父类。这样的结构,又被称为菱形继承。如果此时我们使用C的实例化对象,来调用A中的成员,此时编译器就不知道到底走D-B-A,还是D-C-A。路径在这里可以直接用上面构造函数或析构函数来理解,即到底是D-B-A的路径去调用构造函数,还是走D-C-A去调用构造函数。虚继承的目的是让某个类做出声明,承诺愿意共享它的基类。其中,

WE_BIG的博客 1089

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

c++ 多重继承与虚继承

特性多重继承虚继承用途组合多个类的功能解决菱形继承问题,避免数据冗余和二义性语法基类构造顺序按继承顺序依次构造虚基类由最底层派生类直接构造内存开销可能较高(多个基类实例)通过指针共享基类,略微增加开销最佳实践仅在需要组合多个独立功能时使用多重继承。遇到菱形继承问题时,必须使用虚继承。优先使用组合(对象成员)代替多重继承,降低复杂性。

曾经也曾立志 711

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

多重继承与虚继承编程实验

多重继承与虚继承编程实验基本知识多重继承多重继承下的类作用域虚继承构造函数与虚继承关于本程序示例代码Animal_virtual_baseVers.hvirt-inherit.cpp运行结果 本文是本人大一期间在校学习C++课程时所撰写的实验报告的摘录,由于刚上大学,刚接触计算机编程方面的相关知识,故可能会有很多不足甚至错误的地方,还请各位看官指出及纠正。 本文所涉及到的“教材”指: 电子工业出版社《C++ Primary中文版(第5版)》 如需转载或引用请标明出处。 基本知识 多重继承 多重继承在概念上比

qq_43266093的博客 479

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

C++学习之多重继承与虚继承

一、多重继承 我们知道,在单继承中,派生类的对象中包含了基类部分 和 派生类自定义部分。同样的,在多重继承(multiple inheritance)关系中,派生类的对象包含了每个基类的子对象和自定义成员的子对象。下面是一个多重继承关系: class A{ /* */ }; class B{ /* */ }; class C : public A { /* */ ...

dbbaq24022的专栏 267

大型程序的工具——多重继承与虚继承

一、多重继承与虚继承 1、多重继承   在派生类的派生列表中可以包含多个基类,每个基类包含一个可选的访问说明符。   多重继承的派生列表也只能包含已经定义过的类,而且这些类不能是final的。对于派生类能够继承的基类个数,C++没有进行特殊规定;但是在某个给定的派生列表中,同一个基类只能出现一次。 1)多重继承的派生类从每个基类中继承状态   在多重继承关系中,派生类的对象包含有每个基类...

weixin_30737363的博客 131

C++中多重继承与虚继承资料的整理

最近在看C++的东西,看了好多多重继承的问题,有些说的很简单,有的说的很繁琐,把资料整理一下。在多重继承中,基类的构造函数的调用次序既不受派生类构造函数初始化列表中出现的基类构造函数的影响,也不受基类在构造函数初始化列表中的出现次序的影响,它按照基类在类派生列表中的出现次序依次调用相应的基类构造函数。析构顺序与构造顺序逆序进行。        多重继承中,派生类的指针或引用可以转换为其任意基类的

肖氏专栏 1335

c++多重继承与虚继承

待续...

linux系统、网络编程和分布式计算 964

c++primer 笔记03多重继承与虚继承

18.3多重继承与虚继承 多个直接基类中产生派生类的能力,多重继承的派生类继承了所有父类的属性 18.3.1多重继承 派生类的派生列表中可以包含多个基类,但这些类不能是final的 class ZooAnimal {}; class Endangered{}; class Bear:public ZooAnimal{}; class Panda:public Bear,public Endangered{/**/}; 多重继承的派生类从每个基类中继承状态 Pandda对象的概念结构 {

weixin_46646888的博客 361

《C++继承篇》之多继承、多重继承与虚继承

在学习过程中,知识点的总结都是由发现问题到解决问题的过程中对知识理解和认识的升华。闲话少叙,下面围绕具体的栗子详细介绍C++学习之继承篇(多继承、多重继承与虚继承) 目录:1-1多继承1-2多继承代码2-1多重继承2-2多重继承代码3-1虚继承3-2虚继承代码 1-1多继承 基础知识:多继承是指一个子类继承多个父类。多继承对父类的个数没有限制,继承方式可以是公共继承、保护继承和私有继承。 不写继承方式,则默认是private继承。 直接给出多继承的模板格式太过死板,也不利于读者理解,下面通过一个栗子来引出

ZJ卑微小媛的博客 801

C++进阶(一):多重继承与虚继承

虚继承这块是我同事在重构UI这块遇到的一个bug,一开始以为虚继承可以简单使用为了省一份内存,并解决二义性问题,但还是有陷阱,主要在初始化构造顺序问题上。

weixin_44200074的博客 801

C++多重继承与虚继承分析

同样的,在多重继承(multiple inheritance)关系中,派生类的对象包含了每个基类的子对象和自定义成员的子对象。通过在派生列表中添加virtual(关键字public和virtual的顺序随意)指定A为虚基类,B和C将共享A的同一份实例,这样在D的对象中也将只有A的唯一一份实例,所以A的成员可以被直接访问,并且不会产生二义性。和 d.B::print();构造一个派生类对象将首先构造它的所有基类子对象,其中基类的构造顺序与派生列表中基类的出现顺序保持一致,即B –> A –> C –> D。

m0_74824496的博客 145

C++ 多重继承、虚继承与虚基类

前言:虚继承是为了解决多重继承出现菱形继承时出现的问题。例如:类B、C分别继承了类A。类D多重继承类B和C的时候,类A中的数据就会在类D中存在多份。通过声明继承关系的时候加上virtual关键字可以实现虚继承。 一、多重继承 单重继承——一个派生类最多只能有一个基类 多重继承——一个派生类可以有多个基类 class 类名: 继承方式 基类1,继承方式 基类2,…. {….}; 派生类同...

Magnum的博客 3907

C++学习笔记十九-多重继承与虚继承

概述:多重继承是从多于一个直接基类派生类的能力,多重继承的派生类继承其所有父类的属性。 一、多重继承的定义 1. 由逗号分隔的基类列表: class Panda : public Bear, public Endangered { }; 派生类为每个基类(显式或隐式地)指定了访问级别——public、protected 或...

anhuang5101的博客 226
上一篇: 虚函数(C++)
下一篇: Android 图形系统SurfaceFlinger
learnerz
博客等级 码龄16年 51粉丝 19原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值