说明:在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. Mysql经常使用函数

    -----------------------------字符串函数--------------------------------------- ----拼接字符串.不论什么字符串与null拼接为n ...

  2. android电池管理系统

    原文:http://www.2cto.com/kf/201408/326462.html 1.概述 随着移动智能设备的快速发屏,电池的续航能力在很大情况下诱导了大众消费者的购买选择,android系统 ...

  3. 关于OpenFileDialog的使用 2(转)

    关于OpenFileDialog的使用 (2010-03-25 12:51:33) 标签: 杂谈 分类: WinForm 1. OpenFileDialog控件有以下基本属性 InitialDirec ...

  4. LCT教程

    lct是一种动态树,用来维护一些动态加边删边的操作的东西.他主要用到几个操作,其实这个算法和树链刨分有点像,但是不能用线段树简单维护,所以我们要用多棵平衡树来维护树上的一个个子树,然后就进行一些很秀的 ...

  5. XMLHttpRequest 对象-回调函数

    回调函数是一种以参数形式传递给另一个函数的函数. 如果您的网站上存在多个 AJAX 任务,那么您应该为创建 XMLHttpRequest 对象编写一个标准的函数,并为每个 AJAX 任务调用该函数. ...

  6. Flink之Stateful Operators

    Implementing Stateful Functions source function的stateful看官网,要加lock Declaring Keyed State at the Runt ...

  7. 9.15NOIP模拟题

    GRYZ 模拟考试套题 9.15 gryz信息组专场 题目名称 最初的最初 太 妃 糖 可执行文件名 eat hwc dance sugar 输入文件 eat.in hwc.in dance.in s ...

  8. Java高质量20问

    问题一:在多线程环境中使用HashMap会有什么问题?在什么情况下使用get()方法会产生无限循环? HashMap本身没有什么问题,有没有问题取决于你是如何使用它的.比如,你在一个线程里初始化了一个 ...

  9. BZOJ 2592 随机化(伪)

    思路: 放yousiki大爷题解 http://yousiki.net/index.php/archives/82/ 我写的是随机化 既然gzz证了最终答案的上界是O(N)的 那么我们可以n^2枚举所 ...

  10. Android生命周期回顾

    先回顾生命周期 Activity一共有3中状态 运行中 Activity位于前台,并具有用户焦点 暂停 另一个Activity位于屏幕前台并具有用户焦点,但此Activity仍可见.也就是说,另一个A ...