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

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, 纯虚函数不能实化化,但可以声明指针。

C++的虚基类,抽象类,虚函数,纯虚函数,virtual相关推荐

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  8. 理解虚基类、虚函数与纯虚函数的概念

    引言 一直以来都没有写过一篇关于概念性的文章,因为我觉得这些概念性的东西书本上都有并且说的也很详细写来也无用,今天突发奇想想写 一写,下面就和大家讨论一下虚基类.虚函数与纯虚函数,一看名字就让人很容易 ...

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

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

最新文章

  1. ruby实时查看日志
  2. 自学Python从哪学方面入手?
  3. Oracle内核参数介绍
  4. 用一个创业故事串起操作系统原理(二)
  5. java数组简单介绍以及其方法
  6. Spring 自定义注解使用案例 首先创建一个注解@interface
  7. [实用]DNS解析命令,静静地学会【转载】
  8. zk ui_高级ZK:异步UI更新和后台处理–第2部分
  9. wordpress WBOLT 百度推送管理 3.4.6 Pro
  10. Git笔记(11) 分支简介
  11. 一统江湖的大前端(7)React.js-从开发者到工程师
  12. SQL 优化极简法则,你掌握几个?
  13. python基本数据类型(一)-python3.0学习笔记
  14. iOS xcode4 编译环境
  15. Mendeley从已有文献找相关文献的方法
  16. linux 桌面什么图标好看,Xenlism WildFire:Linux桌面下的一款漂亮图标主题
  17. 通过VNC远程连接ubuntu桌面(多种客户端连接方式)
  18. 计算机硬盘根目录是什么,解决方案:硬盘根目录是什么意思?它存储在哪里?...
  19. 百宝云常用运算符教程
  20. 叁德悟带你认识四大名香

热门文章

  1. StringUtils 工具类常用方法汇总 1(判空、转换、移除、替换、反转)
  2. 「hdu6681」Rikka with Cake【线段树】
  3. 如何顺利通过毕业答辩
  4. 【代码笔记】iOS-钢琴小游戏
  5. 宝马全球首发全新一代5系轿车和纯电动i5
  6. 【THREE.JS】网页中的炫酷3D
  7. 服务器端Java开发人员Kotlin简介
  8. 对话:人狠话不多 vs 中间件小姐姐
  9. ios备份备忘录到android,如何将iPhone备忘录备份到iCloud?
  10. 为什么我们需要去中心化存储?