http://blog.csdn.net/wangxingbao4227/article/details/6772579

C++中虚拟继承的概念

为了解决从不同途径继承来的同名的数据成员在内存中有不同的拷贝造成数据不一致问题,将共同基类设置为虚基类

这时从不同的路径继承过来的同名数据成员在内存中就只有一个拷贝,同一个函数名也只有一个映射。这样不仅就解决了二义性问题,也节省了内存,避免了数据不一致的问题。

class 派生类名:virtual 继承方式  基类名
virtual是关键字,声明该基类为派生类的虚基类。
在多继承情况下,虚基类关键字的作用范围和继承方式关键字相同,只对紧跟其后的基类起作用。
声明了虚基类之后,虚基类在进一步派生过程中始终和派生类一起,维护同一个基类子对象的拷贝。

C++虚拟继承

◇概念:

C++使用虚拟继承(Virtual Inheritance),解决从不同途径继承来的同名的数据成员在内存中有不同的拷贝造成数据不一致问题,将共同基类设置为虚基类。

这时从不同的路径继承过来的同名数据成员在内存中就只有一个拷贝,同一个函数名也只有一个映射。

◇解决问题:

解决了二义性问题,也节省了内存,避免了数据不一致的问题。
 
◇同义词: 
虚基类(把一个动词当成一个名词而已)
当在多条继承路径上有一个公共的基类,在这些路径中的某几条汇合处,这个公共的基类就会产生多个实例(或多个副本),若只想保存这个基类的一个实例,可以将这个公共基类说明为虚基类。

◇语法:

class 派生类: virtual 基类1,virtual 基类2,...,virtual 基类n

{

...//派生类成员声明

};

◇执行顺序

首先执行虚基类的构造函数,多个虚基类的构造函数按照被继承的顺序构造;

执行基类的构造函数,多个基类的构造函数按照被继承的顺序构造;

执行成员对象的构造函数,多个成员对象的构造函数按照申明的顺序构造;

执行派生类自己的构造函数;

析构以与构造相反的顺序执行;

mark

从虚基类直接或间接派生的派生类中的构造函数的成员初始化列表中都要列出对虚基类构造函数的调用。但只有用于建立对象的最派生类的构造函数调用虚基类的构造函数,而该派生类的所有基类中列出的对虚基类的构造函数的调用在执行中被忽略,从而保证对虚基类子对象只初始化一次。

在一个成员初始化列表中同时出现对虚基类和非虚基类构造函数的调用时,虚基类的构造函数先于非虚基类的构造函数执行。

◇因果:

多重继承->二义性->虚拟继承解决

◇二义性:

#include <iostream>
using namespace std;
//Base
class Base
{
public:
Base(){cout << "Base called..."<< endl;}
void print(){cout << "Base print..." <<endl;}
private:
};

//Sub
class Sub //定义一个类 Sub
{
public:
Sub(){cout << "Sub called..." << endl;}
void print(){cout << "Sub print..." << endl;}
private:
};

//Child
class Child : public Base,public Sub//定义一个类Child 分别继承自 Base ,Sub
{                //先调用Base的构造函数 由继承的顺序决定
public:
Child(){cout << "Child called..." << endl;}

private:
};

int main(int argc, char* argv[])
{
Child c;

//不能这样使用,会产生二意性,VC下error C2385
//c.print();

//只能这样使用
c.Base::print();
c.Sub::print();

system("pause");
return 0;
}

◇多重继承:

#include <iostream>
using namespace std;

class Base
{
public:
Base(){cout << "Base called " << endl;}
void print(){cout << "Base print" <<endl;}
};

class Sub :public Base
{
public:
Sub(){cout << "Sub called" << endl;}
void print(){cout << "Sub print" <<endl;}
private:
};

class Gchild : public Sub
{
public:
Gchild(){cout << "Gchild called" << endl;}
void print(){cout << "Gchild print" <<endl;}
};

int main(int argc, char* argv[])
{
Gchild d;

//这里可以这样使用
d.print();      //调用自己的print函数,掩盖父类的print函数

//也可以这样使用
d.Base::print();
d.Sub::print();   //调用自己的print函数,掩盖父类的print函数

system("pause");
return 0;
}

//这样使用  不存在二义性

 

◇虚拟继承

在派生类继承基类时,加上一个virtual关键词则为虚拟继承

#include <iostream>
using namespace std;

int gFlag = 0;

class Base
{
public:
    Base(){cout << "Base called : " << gFlag++ << endl;}
    void print(){cout << "Base print" <<endl;}
};

class Mid1 : virtual public Base //虚基类继承
{
 public:
     Mid1(){cout << "Mid1 called" << endl;}
     private:
};

class Mid2 : virtual public Base //虚基类继承
{
public:
     Mid2(){cout << "Mid2 called" << endl;}
};

class Child:public Mid1, public Mid2
{
 public:
      Child(){cout << "Child called" << endl;}
};

int main(int argc, char* argv[])
{
    Child d;

//这里可以这样使用
    d.print();

//也可以这样使用
   d.Mid1::print();
   d.Mid2::print();

system("pause");
   return 0;
}

//output

◇通过输出的比较

1.在多继承情况下,虚基类关键字的作用范围和继承方式关键字相同,只对紧跟其后的基类起作用。
2.声明了虚基类之后,虚基类在进一步派生过程中始终和派生类一起,维护同一个基类子对象的拷贝。
3.观察类构造函数的构造顺序,拷贝也只有一份。
 
◇与虚函数关系 
虚拟继承与虚函数有一定相似的地方,但他们之间是绝对没有任何联系的。
再想一次:虚拟继承,虚基类,虚函数。

C++学习之虚继承的更多相关文章

  1. (C/C++学习)5.C++中的虚继承-虚函数-多态解析

    说明:在C++学习的过程中,虚继承-虚函数经常是初学者容易产生误解的两个概念,它们与C++中多态形成的关系,也是很多初学者经常产生困惑的地方,这篇文章将依次分别对三者进行解析,并讲述其之间的联系与不同 ...

  2. C++ Primer 学习笔记_95_用于大型程序的工具 --多重继承与虚继承

    用于大型程序的工具 --多重继承与虚继承 引言: 大多数应用程序使用单个基类的公用继承,可是,在某些情况下,单继承是不够用的,由于可能无法为问题域建模,或者会对模型带来不必要的复杂性. 在这些情况下, ...

  3. C++学习之路(十):虚继承引入的执行效率

    这篇文章不知道取啥名字了,暂且这样叫,直接看场景就明白了.节选自<深度探索C++对象模型> Point3d origin, *pt = &origin; (1)origin.x = ...

  4. C++学习之虚函数继承和虚继承

    虚函数的定义要遵循以下重要规则: 1.如果虚函数在基类与派生类中出现,仅仅是名字相同,而形式参数不同,或者是返回类型不同,那么即使加上了virtual关键字,也是不会进行晚绑定的. 2.只有类的成员函 ...

  5. C++ 从内存的角度,学习虚继承机制

    测试代码 #include <stdio.h> struct AA { char b; char b1; int b3; char b2; }; class A { public: A() ...

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

    一.多重继承 我们知道,在单继承中,派生类的对象中包含了基类部分 和 派生类自定义部分.同样的,在多重继承(multiple inheritance)关系中,派生类的对象包含了每个基类的子对象和自定义 ...

  7. C++ 继承之虚继承与普通继承的内存分布

    仅供互相学习,请勿喷,有观点欢迎指出~ class A { virtual void aa(){}; }; class B : public virtual A { ]; //加入一个变量是为了看清楚 ...

  8. 虚继承之单继承的内存布局(VC在编译时会把vfptr放到类的头部,这和Delphi完全一致)

    C++2.0以后全面支持虚函数与虚继承,这两个特性的引入为C++增强了不少功能,也引入了不少烦恼.虚函数与虚继承有哪些特性,今天就不记录了,如果能搞了解一下编译器是如何实现虚函数和虚继承,它们在类的内 ...

  9. c++学习笔记之继承篇

    title: c++学习笔记之继承篇 date: 2017-03-26 16:36:33 tags: [c++,继承,public,virtual,private,protected] categor ...

随机推荐

  1. No architectures to compile for (ONLY_ACTIVE_ARCH=YES, active arch=x86_64, VALID_ARCHS=armv7 armv7s)

    In Build Settings are: Architectures: Starndard (armv7, armv7s) Base SDK: Latest iOS (iOS 6.0) Build ...

  2. PHP上传图片

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  3. 认识和理解css布局中的BFC

    认识和理解css布局中的BFC BFC的定义 是 W3C CSS 2.1 规范中的一个概念,它决定了元素如何对其内容进行定位,以及与其他元素的关系和相互作用. Block Formatting Con ...

  4. 向前辈致敬 strspn

    把8位的CHAR型数据分解为:前5位和后3位,这样2^5 = 32个CHAR型数+值就可表示所有的CHAR型数据 这样做的好处:在给出子串后,不用比较256次,最多比较32次即可判断出是否一个数在子串 ...

  5. /etc/ld.so.conf详解

    /etc/ld.so.conf 此文件记录了编译时使用的动态库的路径,也就是加载so库的路径.    默认情况下,编译器只会使用/lib和/usr/lib这两个目录下的库文件,而通常通过源码包进行安装 ...

  6. iOS 相互引用引起内存泄露问题说明

    release动作只会对自身计数减一,而不会对属性发出release消息,只有该对象的引用计数为0,系统才会对该对象的所有属性发出release消息 类A的属性有类B,类B的属性有类A,而且两者都是强 ...

  7. result 相关

    1.dispatcher 2.redirect 3.chain 4.redirectAction 5.freemarker 6.httpheader 7.stream 8.velocity 9.xsl ...

  8. Assertion failure in -[UIView layoutSublayersOfLayer:]

    Assertion failure in -[UIView layoutSublayersOfLayer:], /SourceCache/UIKit/UIKit-2935.137/UIView.m:8 ...

  9. JavaSE复习日记 : Java操作符\关系运算符\逻辑运算符\赋值运算符\字符串连接符\三元运算符

    // Java操作符\关系运算符\逻辑运算符\赋值运算符\字符串连接符\三元运算符 /* * 一 Java操作符 * * Java操作符都有哪些? * +, -, *, /, %, ++, -- * ...

  10. HOOK API(二)—— HOOK自己程序的 MessageBox

    HOOK API(二) —— HOOK自己程序的 MessageBox 0x00 前言 以下将给出一个简单的例子,作为HOOK API的入门.这里是HOOK 自己程序的MessageBox,即将自己程 ...