说明:在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. LeetCode 290. Word Pattern (词语模式)

    Given a pattern and a string str, find if str follows the same pattern. Here follow means a full mat ...

  2. 5分钟APIG实战: 使用Rust语言快速构建API能力开放

    序言:Rust语言简介 参与过C/C++大型项目的同学可能都经历过因为Null Pointer.Memory Leak等问题“被” 加班了不知道多少个晚上.别沮丧,你不是一个人,Mozilla Fir ...

  3. HDUOJ--2121--Ice_cream’s world II【朱刘算法】不定根最小树形图

    链接:http://acm.hdu.edu.cn/showproblem.php? pid=2121 题意:n个顶点,m条边,求从某一点起建立有向图最小生成树而且花费最小.输出最小花费和根节点下标. ...

  4. es bulk 批量删除

    bulk [root@hadoop2 ~]# cat bulk.del.es.json {"delete":{"_index":"direct_vot ...

  5. 转 linux/unix学习经典书籍

    都是一些链接. 1. Linux网络编程经典书籍推荐 http://blog.csdn.net/zhangpeng_linux/article/details/7001970 2. C语言经典著作导读 ...

  6. 【知识总结】扩展卢卡斯定理(exLucas)

    扩展卢卡斯定理用于求如下式子(其中\(p\)不一定是质数): \[C_n^m\ mod\ p\] 我们将这个问题由总体到局部地分为三个层次解决. 层次一:原问题 首先对\(p\)进行质因数分解: \[ ...

  7. 【洛谷2469/BZOJ1927】[SDOI2010]星际竞速(费用流/最小路径覆盖)

    题目: 洛谷2469 分析: 把题目翻译成人话:给一个带边权的DAG,求一个路径覆盖方案使路径边权总和最小.从点\(i\)开始的路径需要额外加上\(A_i\)的权值. 回xian忆chang一xue下 ...

  8. 5CSS之字体font-family

    ---------------------------------------------------------------------------------------------------- ...

  9. SRM-697-DIV2

    Div2 Medium: DivisibleSetDiv2 Problem Statement      You are given a vector <int> b containing ...

  10. [转]linux sudo 命令

    转自:http://www.cnblogs.com/xiaochaohuashengmi/archive/2011/11/11/2245341.html “Sudo”是Unix/Linux平台上的一个 ...