ref http://blog.csdn.net/owen7500/article/details/52432970?locationNum=4&fps=1
http://blog.csdn.net/u014568921/article/details/52077623?locationNum=5&fps=1

  • 定义:在C++中,在定义公共基类的派生类的时候,如果在继承方式前使用关键字virtual对继承方式限定,这样的继承方式就是虚拟继承,公共的基类成为虚基类。这样,在具有公共基类的、使用了虚拟继承方式的多个派生类的公共派生类中,该基类的成员就只有一份拷贝

多继承(Multiple Inheritance)是指从多个直接基类中产生派生类的能力,多继承的派生类继承了所有父类的成员。尽管概念上非常简单,但是多个基类的相互交织可能会带来错综复杂的设计问题,命名冲突就是不可回避的一个。

多继承时很容易产生命名冲突,即使我们很小心地将所有类中的成员变量和成员函数都命名为不同的名字,命名冲突依然有可能发生,比如典型的是菱形继承,如下图所示:

图1:菱形继承 注:图中应该都为向下箭头

类 A 派生出类 B 和类 C,类 D 继承自类 B 和类 C,这个时候类 A 中的成员变量和成员函数继承到类 D 中变成了两份,一份来自 A–>B–>D 这条路径,另一份来自 A–>C–>D 这条路径。

在一个派生类中保留间接基类的多份同名成员,虽然可以在不同的成员变量中分别存放不同的数据,但大多数情况下这是多余的:因为保留多份成员变量不仅占用较多的存储空间,还容易产生命名冲突。假如类 A 有一个成员变量 a,那么在类 D 中直接访问 a 就会产生歧义,编译器不知道它究竟来自 A –>B–>D 这条路径,还是来自 A–>C–>D 这条路径。下面是菱形继承的具体实现:
//间接基类A
class A{
protected:
int m_a;
};
//直接基类B
class B: public A{
protected:
int m_b;
};
//直接基类C
class C: public A{
protected:
int m_c;
};
//派生类D
class D: public B, public C{
public:
void seta(int a){ m_a = a; } //命名冲突
void setb(int b){ m_b = b; } //正确
void setc(int c){ m_c = c; } //正确
void setd(int d){ m_d = d; } //正确
private:
int m_d;
};
int main(){
D d;
return 0;
}
这段代码实现了上图所示的菱形继承,第 25 行代码试图直接访问成员变量 m_a,结果发生了错误,因为类 B 和类 C 中都有成员变量 m_a(从 A 类继承而来),编译器不知道选用哪一个,所以产生了歧义。

为了消除歧义,我们可以在 m_a 的前面指明它具体来自哪个类:
void seta(int a){ B::m_a = a; }
这样表示使用 B 类的 m_a。当然也可以使用 C 类的:
void seta(int a){ C::m_a = a; }
虚继承(Virtual Inheritance)

为了解决多继承时的命名冲突和冗余数据问题,C++ 提出了虚继承,使得在派生类中只保留一份间接基类的成员。

在继承方式前面加上 virtual 关键字就是虚继承,请看下面的例子:
//间接基类A
class A{
protected:
int m_a;
};
//直接基类B
class B: virtual public A{ //虚继承
protected:
int m_b;
};
//直接基类C
class C: virtual public A{ //虚继承
protected:
int m_c;
};
//派生类D
class D: public B, public C{
public:
void seta(int a){ m_a = a; } //正确
void setb(int b){ m_b = b; } //正确
void setc(int c){ m_c = c; } //正确
void setd(int d){ m_d = d; } //正确
private:
int m_d;
};
int main(){
D d;
return 0;
}
这段代码使用虚继承重新实现了上图所示的菱形继承,这样在派生类 D 中就只保留了一份成员变量 m_a,直接访问就不会再有歧义了。

虚继承的目的是让某个类做出声明,承诺愿意共享它的基类。其中,这个被共享的基类就称为虚基类(Virtual Base Class),本例中的 A 就是一个虚基类。在这种机制下,不论虚基类在继承体系中出现了多少次,在派生类中都只包含一份虚基类的成员。

现在让我们重新梳理一下本例的继承关系,如下图所示:

图2:使用虚继承解决菱形继承中的命名冲突问题

观察这个新的继承体系,我们会发现虚继承的一个不太直观的特征:必须在虚派生的真实需求出现前就已经完成虚派生的操作。在上图中,当定义 D 类时才出现了对虚派生的需求,但是如果 B 类和 C 类不是从 A 类虚派生得到的,那么 D 类还是会保留 A 类的两份成员。

换个角度讲,虚派生只影响从指定了虚基类的派生类中进一步派生出来的类,它不会影响派生类本身。

在实际开发中,位于中间层次的基类将其继承声明为虚继承一般不会带来什么问题。通常情况下,使用虚继承的类层次是由一个人或者一个项目组一次性设计完成的。对于一个独立开发的类来说,很少需要基类中的某一个类是虚基类,况且新类的开发者也无法改变已经存在的类体系。

C++标准库中的 iostream 类就是一个虚继承的实际应用案例。iostream 从 istream 和 ostream 直接继承而来,而 istream 和 ostream 又都继承自一个共同的名为 base_ios 的类,是典型的菱形继承。此时 istream 和 ostream 必须采用虚继承,否则将导致 iostream 类中保留两份 base_ios 类的成员。

图3:虚继承在C++标准库中的实际应用
虚基类成员的可见性

因为在虚继承的最终派生类中只保留了一份虚基类的成员,所以该成员可以被直接访问,不会产生二义性。此外,如果虚基类的成员只被一条派生路径覆盖,那么仍然可以直接访问这个被覆盖的成员。但是如果该成员被两条或多条路径覆盖了,那就不能直接访问了,此时必须指明该成员属于哪个类。

以图2中的菱形继承为例,假设 B 定义了一个名为 x 的成员变量,当我们在 D 中直接访问 x 时,会有三种可能性:
如果 B 和 C 中都没有 x 的定义,那么 x 将被解析为 B 的成员,此时不存在二义性。
如果 B 或 C 其中的一个类定义了 x,也不会有二义性,派生类的 x 比虚基类的 x 优先级更高。
如果 B 和 C 中都定义了 x,那么直接访问 x 将产生二义性问题。

可以看到,使用多继承经常会出现二义性问题,必须十分小心。上面的例子是简单的,如果继承的层次再多一些,关系更复杂一些,程序员就很容易陷人迷魂阵,程序的编写、调试和维护工作都会变得更加困难,因此我不提倡在程序中使用多继承,只有在比较简单和不易出现二义性的情况或实在必要时才使用多继承,能用单一继承解决的问题就不要使用多继承。也正是由于这个原因,C++ 之后的很多面向对象的编程语言,例如 Java、C#、PHP 等,都不支持多继承。

(2)虚函数与继承
ref http://www.cnblogs.com/xudong-bupt/p/3570304.html
1.虚函数&&纯虚函数
虚函数为了重载和多态的需要,在基类中是有定义的,即便定义是空,所以子类中可以重写也可以不写基类中的此函数!
纯虚函数在基类中是没有定义的,必须在子类中加以实现,很像java中的接口函数!
2.http://www.cnblogs.com/xudong-bupt/p/3570304.html
- 虚函数(impure virtual)

  C++的虚函数主要作用是“运行时多态”,父类中提供虚函数的实现,为子类提供默认的函数实现。

  子类可以重写父类的虚函数实现子类的特殊化。

  如下就是一个父类中的虚函数:

复制代码
class A
{
public:
virtual void out2(string s)
{
cout<<”A(out2):”<

C++虚继承和虚基类;虚函数与继承相关推荐

  1. 虚继承c语言例子,C/C++ 多继承{虚基类,虚继承,构造顺序,析构顺序}

    C/C++:一个基类继承和多个基类继承的区别 1.对多个基类继承会出现类之间嵌套时出现的同名问题,如果同名变量或者函数出现不在同一层次,则底层派生隐藏外层比如继承基类的同名变量和函数,不会出现二义性, ...

  2. 基类与接口混合继承的声明问题 [C#, Design] -Allen Lee's Magic

    Updated on Friday, November 19, 2004 Written by Allen Lee 1. 问题初现 今天,查看<接口继承的声明问题>一文的反馈,发现Ninp ...

  3. 基类成员函数和派生类成员函数不构成重载

    基类成员和派生类成员的名字一样时会造成遮蔽,这句话对于成员变量很好理解,对于成员函数要引起注意,不管函数的参数如何,只要名字一样就会造成遮蔽.换句话说,基类成员函数和派生类成员函数不会构成重载,如果派 ...

  4. 2021-03-10 模板扩展类调用模板基类成员函数

    侯捷书内 : 扩展模板类 内通过  this->  指模板基类的成员函数,或通过   基类名::  的方式调用 基类成员函数 侯捷书内说: 扩展模板类,找不到模板基类的成员函数 但是在vs201 ...

  5. java派生类如何使用_继承:使用基类或派生类来做东西[关闭]

    这是一个关于应该使用继承方式的简单问题 . 考虑到我必须提供业务逻辑来打印'foo'和'bar'并且我有两个类的层次结构:一个具有仅打印'foo'的方法和另一个首先扩展并具有打印'bar'的方法 . ...

  6. C++中public/private/protected三种继承方式下基类成员对派生类的可见性探讨

     原文: http://blog.csdn.net/zgjxwl/article/details/4804233 1.public继承方式下 #include <iostream>   ...

  7. 第二十九节 C++ 继承之向基类传递参数

    #include <iostream> using namespace std;/*有时,基类存在重载的构造函数,而搞造函数对类的数据成员进行了初始化, * 这种情况下,在继承基类时,需要 ...

  8. 6-1 设计Shape基类及Circle, Rectangle继承类(高教社,《Python编程基础及应用》习题9-7)分数 20

    设计一个基类Shape,包括: 名为sName的属性(图形名称): 构造函数应对sName属性进行初始化. 设计Shape的继承类Rectangle, 包括: 长,宽两个属性: 构造函数调用Shape ...

  9. python编程语言继承_Python面向对象程序设计类的封装与继承用法示例

    本文实例讲述了Python面向对象程序设计类的封装与继承用法.分享给大家供大家参考,具体如下: 访问限制(封装) 1.概念 面向对象语言的三大特征:封装, 继承, 多态. 广义的封装: 类和函数的定义 ...

  10. 封装绑定以BaseDataList , ListControl 为基类的函数库

    可以绑定Datagrid,DataList,DropDownList,RadioList,,,等控件.很想把DataGrid的很多操作都封装了,免得每次copy代码那么累 using System; ...

最新文章

  1. Python变量类型(l整型,长整形,浮点型,复数,列表,元组,字典)学习
  2. 大数据时代 我们还有隐私吗?
  3. .Net 中的反射(反射特性) - Part.3
  4. bzero, memset ,setmem 区别【转】
  5. Python3 爬虫学习笔记 C16【数据储存系列 — Redis】
  6. Redis1 晨考题
  7. pom中导入spingside包
  8. IOS开发学习----给表视图设置缩进级别
  9. 《精通.Net核心技术》
  10. 28岁女博士生被骂“学术媛”,曾以数学建模专业第一名毕业,获牛津数学PhD offer...
  11. python2中的print语句可以不用小括号。_Python基础语法 | 代码规范amp;判断语句amp;循环语句...
  12. 最全的国内外电子书籍网站合集
  13. ISIS路由聚合实验
  14. 打印服务器后台程序没有运行,win10遇到“打印后台程序服务没有运行”的解决方法...
  15. 2021东南亚跨境电商平台之马来西亚热门电商平台排名TOP10
  16. ImportError: No module named osgeo解决办法
  17. 走进施耐德电气无锡工厂,见证自动化研发中心开幕
  18. 【职场篇】游戏开发社招求职面试指南③——面试总结
  19. TDD--01--ATDD、UTDD
  20. 手写笔迹鉴别系统(1)

热门文章

  1. 【DSY】Migration 题解
  2. 一本珍贵的UNIX简史:长期位居亚马逊UNIX榜单前5的这本书
  3. html li圆点单独颜色,如何修改html中列表项li所显示的圆点的颜色?,以及相关样式的设定...
  4. 苹果更新“查找”APP,三星发布新款SmartTag Plus,蓝牙防丢器市场暴增
  5. ImageNet调查报告
  6. idea 插件之MyBatisCodeHelper
  7. python double free_double free or corruption的原因
  8. 解决问题:远程电脑时出现发生身份验证错误,要求的函数不支持。
  9. 安卓注册登录界面示例
  10. 华唯鑫能油解读厨房燃料分类:液化气、甲醇、白油、生物油等等种类多样,各有不同