C++多重继承二义性原理实例解析

在派生类中对基类成员访问应该是唯一的,但是在多继承时,可能会导致对基类某成员访问出现不一致的情况,这就是C++多继承中的二义性。

有两种继承的情况会产生多义性

一、如果一个派生类从多个基类派生,而这些基类又有一个共同的基类,则在对该基类中声明的成员变量进行访问时,可能产生二义性,继承关系如下图所示:

#include <iostream>
using namespace std;

class A{
public:
  int a;
};

class B1 : public A{
public:
  int b1;
};

class B2 : public A{
public:
  int b2;
};

class C : public B1, public B2{
public:
  int c;
};
int main(){
  C c1;
  c1.b1 = 100;
  c1.b2 = 200;
  c1.c = 300;

  c1.a = 500; //报错,不能确定成员变量a具体在那个类

  cout << "end..." << endl;
  system("pause");
  return 0;
}

解决方法:虚继承

注意:C++编译系统在实例化C类时,只会将虚基类A的构造函数调用一次,忽略虚基类的其他派生类(class B1,class B2)对虚继承的构造函数的调用,从而保证了虚基类的数据成员不会被多次初始化。

在虚基类A中有一个虚指针指向一个虚表,虚表中记录了虚基类与本类的地址偏移,通过这个地址偏移可以找到虚基类的成员变量a的地址

#include <iostream>
using namespace std;

class A{
public:
  int a;
};

class B1 : virtual public A{
public:
  int b1;
};

class B2 : virtual public A{
public:
  int b2;
};

class C : public B1, public B2{
public:
  int c1;
};

int main(){
  C c1;
  c1.b1 = 100;
  c1.b2 = 200;
  c1.c1 = 300;

  c1.a = 500; //虚继承使得成员变量a只有一份拷贝,通过虚指针可以确定地址

  cout << "end..." << endl;
  system("pause");
  return 0;
}

二、一个派生类同时继承两个基类,这两个基类存在相同的成员函数

#include<iostream>
using namespace std;

class Base1 {
public:
  void fun() {
    cout << "I am base-1 " << endl;
  };
};

class Base2
{
public:
  void fun() {
    cout << "I am base-2 " << endl;
  };
};

class A: public Base1, public Base2 {
public:
  void print() {
  }
};

int main() {
  A obj;
  obj.fun();  //调用的方法产生歧义
  system("pause");
  return 0;
}

解决方法:使用作用域运算符::来解决,明确指向的方法,例如obj.Base1::fun()

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • C++多重继承引发的重复调用问题与解决方法

    本文实例讲述了C++多重继承引发的重复调用问题与解决方法.分享给大家供大家参考,具体如下: 前面简单介绍了一个C++多重继承功能示例,这里再来分析一个多重继承引发的重复调用问题,先来看看问题代码: #include "stdafx.h" #include<stdlib.h> #include<iostream> using namespace std; class R//祖先类 { private: int r; public: R(int x = 0):r(x

  • C++实现的多重继承功能简单示例

    本文实例讲述了C++实现的多重继承功能.分享给大家供大家参考,具体如下: 多重继承 1. 多重继承即一个类继承了多个基类的属性. 2. 多重继承下派生类的构造函数必须同时负责所有基类构造函数的调用, 3. 派生类构造函数的参数个数,必须满足多个基类初始化的需要. 4. 在多重继承下,当建立派生类对象时,系统首先调用各个基类的构造函数,调用顺序与定义派生类时指定的基类顺序一致. 多重继承范例: #include <iostream> /* run this program using the c

  • C++中的多态与多重继承实现与Java的区别

    多态问题 笔者校招面试时被问到了著名问题「C++ 与 Java 如何实现多态」,然后不幸翻车.过于著名反而没有去准备,只知道跟虚函数表有关.面试之后比较了 C++ 和 Java 多态的实现的异同,一并记录在这里. C++ 多态的虚指针实现 首先讨论 C++. 多态也即子类对父类成员函数进行了重写 (Override) 后,将一个子类指针赋值给父类,再对这个父类指针调用成员函数,会调用子类重写版本的成员函数.简单的例子: class Parent1 { public: virtual void s

  • C++ 多重继承和虚拟继承对象模型、效率分析

    一.多态 C++多态通过继承和动态绑定实现.继承是一种代码或者功能的传承共享,从语言的角度它是外在的.形式上的,极易理解.而动态绑定则是从语言的底层实现保证了多态的发生--在运行期根据基类指针或者引用指向的真实对象类型确定调用的虚函数功能!通过带有虚函数的单一继承我们可以清楚的理解继承的概念.对象模型的分布机制以及动态绑定的发生,即可以完全彻底地理解多态的思想.为了支持多态,语言实现必须在时间和空间上付出额外的代价(毕竟没有免费的晚餐,更何况编译器是毫无感情): 1.类实现时增加了virtual

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

    本文以实例形式较为全面的讲述了C++的多重继承与虚继承,是大家深入学习C++面向对象程序设计所必须要掌握的知识点,具体内容如下: 一.多重继承 我们知道,在单继承中,派生类的对象中包含了基类部分 和 派生类自定义部分.同样的,在多重继承(multiple inheritance)关系中,派生类的对象包含了每个基类的子对象和自定义成员的子对象.下面是一个多重继承关系图: class A{ /* */ }; class B{ /* */ }; class C : public A { /* */ }

  • 深入解析C++中类的多重继承

    C++类的多继承 在前面的例子中,派生类都只有一个基类,称为单继承.除此之外,C++也支持多继承,即一个派生类可以有两个或多个基类. 多继承容易让代码逻辑复杂.思路混乱,一直备受争议,中小型项目中较少使用,后来的 Java.C#.PHP 等干脆取消了多继承.想快速学习C++的读者可以不必细读. 多继承的语法也很简单,将多个基类用逗号隔开即可.例如已声明了类A.类B和类C,那么可以这样来声明派生类D: class D: public A, private B, protected C{ //类D新

  • C++多重继承二义性原理实例解析

    在派生类中对基类成员访问应该是唯一的,但是在多继承时,可能会导致对基类某成员访问出现不一致的情况,这就是C++多继承中的二义性. 有两种继承的情况会产生多义性 一.如果一个派生类从多个基类派生,而这些基类又有一个共同的基类,则在对该基类中声明的成员变量进行访问时,可能产生二义性,继承关系如下图所示: #include <iostream> using namespace std; class A{ public: int a; }; class B1 : public A{ public: i

  • hashset去除重复值原理实例解析

    Java中的set是一个不包含重复元素的集合,确切地说,是不包含e1.equals(e2)的元素对.Set中允许添加null.Set不能保证集合里元素的顺序. 在往set中添加元素时,如果指定元素不存在,则添加成功.也就是说,如果set中不存在(e==null?e1==null:e.queals(e1))的元素e1,则e1能添加到set中. 下面以set的一个实现类HashSet为例,简单介绍一下set不重复实现的原理: package com.darren.test.overide; publ

  • JavaScript事件冒泡机制原理实例解析

    这篇文章主要介绍了JavaScript事件冒泡机制原理实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 DOM事件流(event flow )存在三个阶段:事件捕获阶段.处于目标阶段.事件冒泡阶段,事件冒泡顺序是由内到外进行事件传播,事件冒泡是由IE开发团队提出来的,即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播. 听了简介介绍之后,您可能不理解,所以举个例子: <html> <head>

  • Java等待唤醒机制原理实例解析

    这篇文章主要介绍了Java等待唤醒机制原理实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 线程的状态 首先了解一下什么是线程的状态,线程状态就是当线程被创建(new),并且启动(start)后,它不是一启动就进入了执行状态(run),也不是一直都处于执行状态. 这里说一下Java 的Thread类里面有一个State方法,这个方法里面涵盖了6种线程的状态,如下: public enum State { // 尚未启动的线程的线程状态.

  • Spring核心容器IOC原理实例解析

    这篇文章主要介绍了Spring核心容器IOC原理实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一 .BeanFactory Spring Bean 的创建是典型的工厂模式,这一系列的 Bean 工厂,也即 IOC 容器为开发者管理对象 间的依赖关系提供了很多便利和基础服务.最基本的 IOC 容器接口 BeanFactory,来看一下它的源码: public interface BeanFactory { //对 FactoryBean

  • JavaScript函数Call、Apply原理实例解析

    这篇文章主要介绍了JavaScript函数Call.Apply原理实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一.方法重用 使用 call() 方法,您可以编写能够在不同对象上使用的方法. 1.函数是对象方法 在 JavaScript 中,函数是对象的方法. 如果一个函数不是 JavaScript 对象的方法,那么它就是全局对象的函数(参见前一章). 下面的例子创建了带有三个属性的对象(firstName.lastName.full

  • sql注入报错之注入原理实例解析

    目录 前言 0x01 0x02 0x03 总结 前言 我相信很多小伙伴在玩sql注入报错注入时都会有一个疑问,为什么这么写就会报错?曾经我去查询的时候,也没有找到满意的答案,时隔几个月终于找到搞清楚原理,特此记录,也希望后来的小伙伴能够少走弯路 0x01 我们先来看一看现象,我这里有一个users表,里面有五条数据: 然后用我们的报错语句查询一下: select count(*),(concat(floor(rand()*2),(select version())))x from users g

  • MySQL InnoDB锁类型及锁原理实例解析

    目录 锁 共享锁 排他锁 意向锁 记录锁 间隙锁 临键锁 死锁 死锁产生条件 行锁发生死锁 表锁发生死锁 锁的释放 事务阻塞 死锁的避免 锁的日志 行锁的原理 不带任何索引的表 带主键索引的表 带唯一索引的表 结论 1.表必定有索引 2.唯一索引数据行加锁,主键索引同样被锁 锁 锁是用来解决事务对数据的并发访问的问题的.MyISAM支持表锁,InnoDB同时支持表锁和行锁. 表加锁语法: lock tables xxx read; lock tables xxx write; unlock ta

  • Mybatis Mapper接口工作原理实例解析

    KeyWords: Mybatis 原理,源码,Mybatis Mapper 接口实现类,代理模式,动态代理,Java动态代理, Proxy.newProxyInstance,Mapper 映射,Mapper 实现 MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及高级映射.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.我们在使用 Mybaits 进行 ,通常只需要定义几个 Mapper 接口,然后在编写一个 xml 文件,我们在配置文件中

  • Vue数据双向绑定原理实例解析

    Vue数据双向绑定原理是通过数据劫持结合发布者-订阅者模式的方式来实现的,首先是对数据进行监听,然后当监听的属性发生变化时则告诉订阅者是否要更新,若更新就会执行对应的更新函数从而更新视图 MVC模式 以往的MVC模式是单向绑定,即Model绑定到View,当我们用JavaScript代码更新Model时,View就会自动更新 MVVM模式 MVVM模式就是Model–View–ViewModel模式.它实现了View的变动,自动反映在 ViewModel,反之亦然.对于双向绑定的理解,就是用户更

随机推荐