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

一.虚继承

1.在多继承中,对于多个父类的数据及函数成员,虽然有时候把他们全部继承下来是有必要的,比如当这些成员都不同的时候。但在大多数的情况下,比如当多个父类之中的成员有重叠的地方时,因为保留多份数据成员的拷贝,不仅占有了较多的存储空间,还增加了访问的难度(由于继承了来自多个父类的同名数据成员,访问时需要加上父类的作用域,比如“父类名::成员”),因此,在实际的继承中是没必要的。而虚继承则可以完美的解决这一问题。

2.在虚继承中,被虚继承的类叫做虚基类,虚基类是需要设计和抽象的,它应当提取多继承父类中重叠的部分作为成员,虚继承是对继承的一种扩展。

示例1:

 #include<iostream>
using namespace std; class furniture
{
public:
furniture(float l,float wi,float we)
:len(l),wid(wi),weight(we)
{}
void dis()
{
cout<<"len = "<<len<<endl;
cout<<"wid = "<<wid<<endl;
cout<<"weight="<<weight<<endl;
}
protected:
float len;
float wid;
float weight;
}; //+++++++++++++++++++++++++ class bed:virtual public furniture
{
public:
bed(float l,float wi,float we)
:furniture(l,wi,we)
{} void sleep(){cout<<"go to sleep!!!!!"<<endl;}
}; //+++++++++++++++++++++++++ class sofa:virtual public furniture
{
public:
sofa(float l,float wi,float we)
:furniture(l,wi,we)
{} void sit(){cout<<"go to have a rest!!!!!"<<endl;}
}; //+++++++++++++++++++++++++ class sofabed:public bed,public sofa
{
public:
sofabed(float l,float wi,float we)
:furniture(l,wi,we),bed(1,2,3),sofa(1,2,3)
{}
}; int main()
{
bed b(1,2,3);
b.sleep();
b.dis();
sofa s(2,3,4);
s.sit();
s.dis();
sofabed sb(4,5,6);
sb.sleep();
sb.sit();
sb.dis();
return 0;
}

查看代码

程序运行结果:

在本例中,如果仅仅采用的是多继承而非虚继承,如下代码所示:

 #include<iostream>
using namespace std; class bed
{
public:
bed(float l,float wi,float we)
:len(l),wid(wi),weight(we)
{} void sleep()
{
cout<<"go to sleep!!!!!"<<endl;
} void dis()
{
cout<<"len = "<<len<<endl;
cout<<"wid = "<<wid<<endl;
cout<<"weight = "<<weight<<endl;
}
protected:
float len;
float wid;
float weight;
};
//+++++++++++++++++++++++++++++
class sofa
{
public:
sofa(float l,float wi,float we)
:len(l),wid(wi),weight(we)
{}
void sit()
{
cout<<"go to have a rest!!!!!"<<endl;
}
void dis()
{
cout<<"len = "<<len<<endl;
cout<<"wid = "<<wid<<endl;
cout<<"weight = "<<weight<<endl;
}
protected:
float len;
float wid;
float weight; };
//+++++++++++++++++++++++++++
class sofabed:public bed,public sofa
{
public:
sofabed(float l,float wi,float we)
:bed(l,wi,we),sofa(l,wi,we)
{}
};
//+++++++++++++++++++++++++++
int main()
{
bed b(1,2,3);
b.sleep();
b.dis();
sofa s(2,3,4);
s.sit();
s.dis();
sofabed sb(5,6,7);
sb.sit();
sb.sleep();
sb.sofa::dis();
sb.bed::dis();
return 0;
}

查看代码

则sb.dis()就有问题了;因为它产生了二义性,编译器不知道该调用哪一个父类的成员函数,而正确做法是加上父类的作用域,这无疑是增加了访问了难度。

结论:多继承带来的数据存储多份,占用内存空间较多,并且访问不便(作用域的增加),采用虚继承可以解决这一问题。

二.纯虚函数

1.纯虚函数的格式:

 class A
{
virtual void func() = 0;
}

2.含有纯虚函数的类为抽象基类,不可创建对象,其存在的意义就是被继承,提供族类的公共接口,

3.纯虚函数只有声明,没有实现,被初始化为0,

4.如果一个类中声明了纯虚函数,而在派生类中没有对该函数定义,则该函数在派生类中仍然为纯虚函数,派生类仍然为纯虚基类,

5.含有虚函数的类,析构函数也应该声明为虚函数,这样在delete父类指针的时候,才会调用子类的析构函数,实现完整析构,

 #include<iostream>
using namespace std; class A
{
public:
A()
{
cout<<"A(){}"<<endl;
}
virtual ~A()
{
cout<<"~A(){}"<<endl;
}
virtual void func() = 0;
};
class B:public A
{
public:
B(){cout<<"B(){}"<<endl;}
~B(){cout<<"~B(){}"<<endl;}
virtual void func()
{
cout<<"B.func(){}"<<endl;
}
};
int main()
{
A*pa = new B;
pa->func();
delete pa;
return 0;
}

查看代码

程序运行结果:

注意:若在此例中,没有将含有虚函数的父类析构函数声明为虚函数,则将不会调用子类的析构函数~B()实现完整析构。

三.多态的实现

1.C++中的多态指的是由于继承而产生的相关的不同的类,其对象对同一消息会做出不同的反应。

2.多态实现的前提是赋值兼容,赋值兼容的内容如下:

a.子类的对象可以赋值给基类的对象,

b.子类的对象可以赋值给基类的引用,

c.子类对象的地址可以赋值给基类的指针(一般用于动多态的实现),

d.在赋值后,子类对象就可以作为基类对象使用,但只能访问从基类继承的成员.

3.动多态的实现条件:

a.父类中有虚函数,

b.子类override(覆写)父类中的虚函数,

c.将子类的对象赋值给父类的指针或引用,由其调用公用接口.

 #include<iostream>
using namespace std; class Shape
{
public:
virtual void draw() = 0;
};
//+++++++++++++++++++
class Circle:public Shape
{
public:
void draw()
{
cout<<"Circle"<<endl;
}
};
//+++++++++++++++++++
class Rect:public Shape
{
public:
void draw()
{
cout<<"Rect"<<endl;
}
};
int main()
{
Circle c;
Rect r;
Shape *p = &c;
p->draw();
p = &r;
p->draw();
return 0;
}

查看代码

注意:C++中的多态一般指动多态,其实C++中函数的重载也是一种多态现象,其通过命名倾轧在编译阶段决定,故称为静多态;而动多态一般是在父子类中在运行阶段决定的。

(C/C++学习)5.C++中的虚继承-虚函数-多态解析的更多相关文章

  1. C++ 由虚基类 虚继承 虚函数 到 虚函数表

    //虚基类:一个类可以在一个类族中既被用作虚基类,也被用作非虚基类. class Base1{ public: Base1(){cout<<"Construct Base1!&q ...

  2. 【集成学习】sklearn中xgboost模块的XGBClassifier函数

    # 常规参数 booster gbtree 树模型做为基分类器(默认) gbliner 线性模型做为基分类器 silent silent=0时,不输出中间过程(默认) silent=1时,输出中间过程 ...

  3. freeertos中关于PendSV中断服务函数的解析

    __asm void xPortPendSVHandler( void ) { extern uxCriticalNesting; extern pxCurrentTCB; extern vTaskS ...

  4. C++ 深入理解 虚继承、多重继承和直接继承

    [摘要] 本文从5段代码实例出发,通过类中类的普通继承,类的虚继承,类的多重继承,多个虚函数类的普通继承.虚继承与多重继承,几个交叉概念,详细的阐释了继承.虚函数与虚继承的基本概念,深入剖析了继承于虚 ...

  5. C/C++ 多继承{虚基类,虚继承,构造顺序,析构顺序}

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

  6. 【c++】多重继承与虚继承

    派生类的构造函数初始化列表将实参分别传递给每个直接基类,其中基类的构造顺序与派生列表中基类的出现顺序保持一致,而与派生类构造函数初始化列表中基类的顺序无关. 类型转换与多个基类 编译器不会在派生类向基 ...

  7. C++之易混淆知识点四---虚函数与虚继承

    C++面向对象中,虚函数与虚继承是两个完全不同的概念. 一.虚函数 C++程序中只要类中含有虚拟函数,编译程序都会为此类生成一个对应的虚拟函数跳转表(vtbl),该虚拟函数跳转表是一个又若干个虚拟函数 ...

  8. C++对象模型:单继承,多继承,虚继承,菱形虚继承,及其内存布局图

    C++目前使用的对象模型: 此模型下,nonstatic数据成员被置于每一个类的对象中,而static数据成员则被置于类对象之外,static和nonstatic函数也都放在类对象之外(通过函数指针指 ...

  9. C++中为什么要用虚函数、指针或引用才能实现多态?

    原文链接:http://blog.csdn.net/zoopang/article/details/14071779 学过C++的都知道,要实现C++的多态性必须要用到虚函数,并且还要使用引用或者指针 ...

随机推荐

  1. Solr基础教程之solrconfig.xml(三)

    前面介绍过schema.xml的一些配置信息,本章介绍solrconfig.xml的配置,以及怎样安装smartcn分词器和IK分词器,并介绍主要的查询语法. 1. solr配置solrconfig. ...

  2. 【iOS开发-32】iOS程序真机调试须要购买调试证书怎么办?

    一.情况 我们在开发iOS程序的时候,一般都是在模拟器上执行查看效果的. 可是,当开完完毕.须要在真机上调试怎么办? 二.官方解决的方法 苹果有为个人和企业开发人员提供调试证书和公布证书.个人版99美 ...

  3. 【CodeForces】166'E

    166’E Tetrahedron You are given a tetrahedron. Let’s mark its vertices with letters A, B, C and D co ...

  4. 解决openresty http客户端不支持https的问题

    OpenResty默认没有提供Http客户端,需要使用第三方提供:当然我们可以通过ngx.location.capture 去方式实现,但它只能发送一个子请求. 第三方基本是以lua-resty-ht ...

  5. min-max容斥小结

    https://www.zybuluo.com/ysner/note/1248287 定义 对于一个集合\(S\), \(\min(S)\)表示其第一个出现的元素(\(or\)最小的元素), \(\m ...

  6. Maven远程中央仓库地址

    阿里云 - http://maven.aliyun.com/nexus/content/groups/public/ Apache Snapshots - https://repository.apa ...

  7. zookeeper单机安装

    安装zookeeper步骤: 1,下载zookeeper http://mirror.bit.edu.cn/apache/zookeeper/zookeeper-3.4.14/ 2,放到合适目录,解压 ...

  8. Android点9图的运用

    在Android UI设计开发中,我们经常会用到一些图标.图片来做背景等. 相信很多同学都会遇到一个问题,就是我们让美工做好一张图,一个图标,呃,看起来挺好看的,但是放进app中,扩大或缩小.在不同分 ...

  9. Dockerfile镜像的制作

    Dockerfile镜像的制作 如果学习Docker,那么制作镜像这一步肯定不能少的,别人给你的是环境,而你自己做的才是你最终需要的东西,接下来就记录一下如何制作一个满足自己的镜像,我们使用docke ...

  10. Combox两级联动会经常出现的错误

    例如: 当我们遇到这种情况:(下拉框的隐藏值和显示值皆为实体类进行绑定值时)下拉框的隐藏值并不能成功获取到. 我们就可以使用下面 的方案来解决 ok ,成功获取到隐藏值. 还有一个,附加解决方案: