引言

一直以来都没有写过一篇关于概念性的文章,因为我觉得这些概念性的东西书本上都有并且说的也很详细写来也无用,今天突发奇想想写 一写,下面就和大家讨论一下虚基类、虚函数与纯虚函数,一看名字就让人很容易觉得混乱。不过不要紧待看完本文后你就会理解了。

正文

虚基类
       在说明其作用前先看一段代码

class A
{
public:
    int iValue;
};

class B:public A
{
public:
    void bPrintf(){cout<<"This is class B"<<endl;};
};

class C:public A
{
public:
    void cPrintf(){cout<<"This is class C"<<endl;};
};

class D:public B,public C
{
public:
    void dPrintf(){cout<<"This is class D"<<endl;};
};

void main()
{
    D d;
    cout<<d.iValue<<endl; //错误,不明确的访问
    cout<<d.A::iValue<<endl; //正确
    cout<<d.B::iValue<<endl; //正确
    cout<<d.C::iValue<<endl; //正确
}

从代码中可以看出类B C都继承了类A的iValue成员,因此类B C都有一个成员变量iValue ,而类D又继承了B C,这样类D就有一个重名的成员 iValue(一个是从类B中继承过来的,一个是从类C中继承过来的).在主函数中调用d.iValue 因为类D有一个重名的成员iValue编译器不知道调用 从谁继承过来的iValue所以就产生的二义性的问题.正确的做法应该是加上作用域限定符 d.B::iValue 表示调用从B类继承过来的iValue。不过 类D的实例中就有多个iValue的实例,就会占用内存空间。所以C++中就引用了虚基类的概念,来解决这个问题。

class A
{
public:
    int iValue;
};

class B:virtual public A
{
public:
    void bPrintf(){cout<<"This is class B"<<endl;};
};

class C:virtual public A
{
public:
    void cPrintf(){cout<<"This is class C"<<endl;};
};

class D:public B,public C
{
public:
    void dPrintf(){cout<<"This is class D"<<endl;};
};

void main()
{
    D d;
    cout<<d.iValue<<endl; //正确
}

在继承的类的前面加上virtual关键字表示被继承的类是一个虚基类,它的被继承成员在派生类中只保留一个实例。例如iValue这个成员,从类 D这个角度上来看,它是从类B与类C继承过来的,而类B C又是从类A继承过来的,但它们只保留一个副本。因此在主函数中调用d.iValue时就不 会产生错误。

虚函数
       还是先看代码

class A
{
public:
    void funPrint(){cout<<"funPrint of class A"<<endl;};
};

class B:public A
{
public:
    void funPrint(){cout<<"funPrint of class B"<<endl;};
};

void main()
{
    A *p; //定义基类的指针
    A a;
    B b;
    p=&a;
    p->funPrint();
    p=&b;
    p->funPrint();
}

大家以为这段代码的输出结果是什么?有的人可能会马上回答funPrint of class A 与 funPrint of class B 因为第一次输出是引用类A的实 例啊,第二次输出是引用类B的实例啊。那么我告诉你这样想就错啦,答案是funPrint of class A 与 funPrint of class A 至于为什么输出 这样的结果不在本文讨论的范围之内;你就记住,不管引用的实例是哪个类的当你调用的时候系统会调用左值那个对象所属类的方法。比如说 上面的代码类A B都有一个funPrint 函数,因为p是一个A类的指针,所以不管你将p指针指向类A或是类B,最终调用的函数都是类A的funPrint 函数。这就是静态联篇,编译器在编译的时候就已经确定好了。可是如果我想实现跟据实例的不同来动态决定调用哪个函数呢?这就须要用到 虚函数(也就是动态联篇)

class A
{
public:
    virtual void funPrint(){cout<<"funPrint of class A"<<endl;};
};

class B:public A
{
public:
    virtual void funPrint(){cout<<"funPrint of class B"<<endl;};
};

void main()
{
    A *p; //定义基类的指针
    A a;
    B b;
    p=&a;
    p->funPrint();
    p=&b;
    p->funPrint();
}

在基类的成员函数前加virtual关键字表示这个函数是一个虚函数,所谓虚函数就是在编译的时候不确定要调用哪个函数,而是动态决定将要调 用哪个函数,要实现虚函数必须派生类的函数名与基类相同,参数名参数类型等也要与基类相同。但派生类中的virtual关键字可以省略,也表 示这是一个虚函数。下面来解决一下代码,声明一个基类的指针(必须是基类,反之则不行)p,把p指向类A的实例a,调用funPrint函数,这 时系统会判断p所指向的实例的类型,如果是A类的实例就调用A类的funPrint函数,如果是B类的实例就调用B类的funPrint函数。

纯虚函数 
    与其叫纯虚函数还不如叫抽象类,它只是声明一个函数但不实现它,让派生类去实现它,其实这也很好理解。

class Vehicle
{
public:
    virtual void PrintTyre()=0; //纯虚函数是这样定义的
};

class Camion:public Vehicle
{
public:
    virtual void PrintTyre(){cout<<"Camion tyre four"<<endl;};
};

class Bike:public Vehicle
{
public:
    virtual void PrintTyre(){cout<<"Bike tyre two"<<endl;};
};

void main()
{
    Camion c;
    Bike b;
    b.PrintTyre();
    c.PrintTyre();
}

如上代码,定义了一个交通工具类(Vehicle),类中有一函数可打印出交通工具的轮胎个数,但交通工具很多轮胎个数自然也就不确定,所以 就把它定义为纯虚函数,也就是光定义函数名不去实现它,类Camion继承了Vehicle并实现了里面的代码,打印出有4个轮胎。Bike类也是一样。 有一点须要注意一下,纯虚函数不能实化化,但可以声明指针。

总结

虚基类 
    1, 一个类可以在一个类族中既被用作虚基类,也被用作非虚基类。 
    2, 在派生类的对象中,同名的虚基类只产生一个虚基类子对象,而某个非虚基类产生各自的子对象。 
    3, 虚基类子对象是由最派生类的构造函数通过调用虚基类的构造函数进行初始化的。 
    4, 最派生类是指在继承结构中建立对象时所指定的类。 
    5, 派生类的构造函数的成员初始化列表中必须列出对虚基类构造函数的调用;如果未列出,则表示使用该虚基类的缺省构造函数。 
    6, 从虚基类直接或间接派生的派生类中的构造函数的成员初始化列表中都要列出对虚基类构造函数的调用。但只有用于建立对象的最派生 类的构造函数调用虚基类的构造函数,而该派生类的所有基类中列出的对虚基类的构造函数的调用在执行中被忽略,从而保证对虚基类子对象 只初始化一次。 
    7, 在一个成员初始化列表中同时出现对虚基类和非虚基类构造函数的调用时,虚基类的构造函数先于非虚基类的构造函数执行。

虚函数 
    1, 虚函数是非静态的、非内联的成员函数,而不能是友元函数,但虚函数可以在另一个类中被声明为友元函数。 
    2, 虚函数声明只能出现在类定义的函数原型声明中,而不能在成员函数的函数体实现的时候声明。 
    3, 一个虚函数无论被公有继承多少次,它仍然保持其虚函数的特性。 
    4, 若类中一个成员函数被说明为虚函数,则该成员函数在派生类中可能有不同的实现。当使用该成员函数操作指针或引用所标识的对象时 ,对该成员函数调用可采用动态联编。 
    5, 定义了虚函数后,程序中声明的指向基类的指针就可以指向其派生类。在执行过程中,该函数可以不断改变它所指向的对象,调用不同 版本的成员函数,而且这些动作都是在运行时动态实现的。虚函数充分体现了面向对象程序设计的动态多态性。 纯虚函数 版本的成员函数,而且这些动作都是在运行时动态实现的。虚函数充分体现了面向对象程序设计的动态多态性。

纯虚函数 
    1, 当在基类中不能为虚函数给出一个有意义的实现时,可以将其声明为纯虚函数,其实现留待派生类完成。 
    2, 纯虚函数的作用是为派生类提供一个一致的接口。

3, 纯虚函数不能实化化,但可以声明指针。

http://www.cnblogs.com/MS-Frank/archive/2008/01/16/1041310.html

http://www.cppblog.com/ElliottZC/archive/2007/07/20/28417.html

理解虚基类、虚函数与纯虚函数的概念相关推荐

  1. java设置虚基类的目的_设置虚基类的目的是( )。

    有pqueue.h如下 #ifndef HEADER_PQUEUE_H#define HEADER_PQUEUE_Htypedef struct_pqueue{    pitem *items;    ...

  2. c++ 虚函数多态、纯虚函数、虚函数表指针、虚基类表指针详解

    文章目录 静态多态.动态多态 虚函数 哪些函数类型不可以被定义成虚函数? 虚函数的访问方式 析构函数中的虚函数 虚函数表指针 vptr 多继承下的虚函数表 虚基类表指针 bptr 纯虚函数 抽象类 虚 ...

  3. 虚函数,虚基类 与纯虚函数 二

    虚函数    还是先看代码 class A { public:     void funPrint(){cout<<"funPrint of class A"<& ...

  4. C++ - 虚基类、虚函数与纯虚函数

    虚基类        在说明其作用前先看一段代码 class A { public:    int iValue; }; class B:public A { public:    void bPri ...

  5. cnbloger: 北岛知寒, C++ - 虚基类、虚函数与纯虚函数; csdner: Hsuxu, C++虚基类的实现机制

    If the author of the article is not allowed to reprint, this article will be deleted C++ - 虚基类.虚函数与纯 ...

  6. C++中的各种“虚“-- 虚函数、纯虚函数、虚继承、虚基类、虚析构、纯虚析构、抽象类讲解

    C++中的各种"虚" 1. 菱形继承 1.1 虚继承 && 虚基类 1.2 虚基类指针(vbptr)&& 虚基类表(vbtable) 2. 多态 2 ...

  7. C++的虚基类,抽象类,虚函数,纯虚函数,virtual

    虚基类 在说明其作用前先看一段代码 class A { public: int iValue; }; class B:public A { public: void bPrintf(){cout< ...

  8. 虚基类、虚函数和纯虚基类

    http://blog.csdn.net/lovemysea/article/details/5298589 首先看一个例子: class Base { public:     virtual voi ...

  9. 虚函数 纯虚函数 虚基类说明

    原文:http://www.cnblogs.com/ms-frank/archive/2008/01/16/1041310.html 虚基类 在说明其作用前先看一段代码 [cpp] view plai ...

最新文章

  1. mogileFS 分布式存储-安装手记
  2. c# usercontrol ,networkcomms3.0 Invoke总结
  3. sphinx 源码阅读之分词,压缩索引,倒排——单词对应的文档ID列表本质和lucene无异 也是外部排序再压缩 解压的时候需要全部扫描doc_ids列表偏移量相加获得最终的文档ID...
  4. 12.figure/subplot多窗口技巧
  5. 函数计算支持应用中心功能
  6. 下一代云原生应用制品管理平台,容器镜像服务企业版优惠进行时
  7. 热血上头!程序员想拍桌子离职的1000个瞬间...
  8. 我们不生产代码,我们只是代码的搬运工
  9. Java区间拆分子集求和,对列表中的数字子集求和
  10. uploadify onSelect
  11. SDI Over IP相关标准及技术简介
  12. win7 修改html文件图标,如何更改文件图标,教您Win7如何更改图标
  13. 大数据加工的方法,主要分为哪几种?
  14. MMC、SD、TF、SDIO、SDMMC简介
  15. 6月小红书博主排行,谁是最佳创作者?
  16. 基于机器学习的Adam 优化算法来提高深层神经网络的训练速度
  17. 【机器学习】马尔可夫链与隐马尔可夫模型(HMM)
  18. Java高并发系列5-线程池
  19. 一位父亲给儿女的九条人生忠告(看看吧,受用一生,适用于任何人)
  20. 深入理解 vue DOM 更新时机

热门文章

  1. 22年全国程序员1月薪资出炉,才知道年薪 40 万以上的有这么多?
  2. CPU天梯图2020年最新,笔记本cpu 4800u 4600u强势
  3. php5安装教程,php5 for windows 安装详解-PHP教程,PHP安装
  4. 永磁同步电机PMSM+感应电机+直流无刷电机BLDC VF变频 双环和三环FOC程序 dsp28335
  5. 利安科技IPO过会:拟募资6.4亿 为李士峰与邱翌夫妻店
  6. Linux系统Wpa_supplicant用法小结(转)
  7. 公司安装了电脑监控是不是侵犯了员工的隐私?
  8. C# HttpWebRequest请求超时解决办法
  9. 这款协同办公软件,解决异地办公难问题
  10. Unreal Engine 虚幻引擎,性能分析,优化(二)