(C/C++学习)5.C++中的虚继承-虚函数-多态解析
说明:在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++中的虚继承-虚函数-多态解析的更多相关文章
- C++ 由虚基类 虚继承 虚函数 到 虚函数表
//虚基类:一个类可以在一个类族中既被用作虚基类,也被用作非虚基类. class Base1{ public: Base1(){cout<<"Construct Base1!&q ...
- 【集成学习】sklearn中xgboost模块的XGBClassifier函数
# 常规参数 booster gbtree 树模型做为基分类器(默认) gbliner 线性模型做为基分类器 silent silent=0时,不输出中间过程(默认) silent=1时,输出中间过程 ...
- freeertos中关于PendSV中断服务函数的解析
__asm void xPortPendSVHandler( void ) { extern uxCriticalNesting; extern pxCurrentTCB; extern vTaskS ...
- C++ 深入理解 虚继承、多重继承和直接继承
[摘要] 本文从5段代码实例出发,通过类中类的普通继承,类的虚继承,类的多重继承,多个虚函数类的普通继承.虚继承与多重继承,几个交叉概念,详细的阐释了继承.虚函数与虚继承的基本概念,深入剖析了继承于虚 ...
- C/C++ 多继承{虚基类,虚继承,构造顺序,析构顺序}
C/C++:一个基类继承和多个基类继承的区别 1.对多个基类继承会出现类之间嵌套时出现的同名问题,如果同名变量或者函数出现不在同一层次,则底层派生隐藏外层比如继承基类的同名变量和函数,不会出现二义性, ...
- 【c++】多重继承与虚继承
派生类的构造函数初始化列表将实参分别传递给每个直接基类,其中基类的构造顺序与派生列表中基类的出现顺序保持一致,而与派生类构造函数初始化列表中基类的顺序无关. 类型转换与多个基类 编译器不会在派生类向基 ...
- C++之易混淆知识点四---虚函数与虚继承
C++面向对象中,虚函数与虚继承是两个完全不同的概念. 一.虚函数 C++程序中只要类中含有虚拟函数,编译程序都会为此类生成一个对应的虚拟函数跳转表(vtbl),该虚拟函数跳转表是一个又若干个虚拟函数 ...
- C++对象模型:单继承,多继承,虚继承,菱形虚继承,及其内存布局图
C++目前使用的对象模型: 此模型下,nonstatic数据成员被置于每一个类的对象中,而static数据成员则被置于类对象之外,static和nonstatic函数也都放在类对象之外(通过函数指针指 ...
- C++中为什么要用虚函数、指针或引用才能实现多态?
原文链接:http://blog.csdn.net/zoopang/article/details/14071779 学过C++的都知道,要实现C++的多态性必须要用到虚函数,并且还要使用引用或者指针 ...
随机推荐
- com.opensymphony.xwork2.inject.DependencyException: com.opensymphony.xwork2.inject.ContainerImpl$Mis
为什么会出现这个错误? 我使用了Maven配置Struts,所以改动Struts配置文件的默认路径,例如以下: <!-- 配置struts --> <filter> <f ...
- ios9--UIImageView的帧动画
// // ViewController.m // 05-UIImageView的帧动画 // #import "ViewController.h" @interface View ...
- [转]Dialog
在Android开发中,我们经常会需要在Android界面上弹出一些对话框,比如询问用户或者让用户选择.这些功能我们叫它Android Dialog对话框,在我们使用Android的过程中,我归纳了一 ...
- Codeforces--633D--Fibonacci-ish (map+去重)(twice)
Fibonacci-ish Time Limit: 3000MS Memory Limit: 524288KB 64bit IO Format: %I64d & %I64u Su ...
- nodejs在windows下的安装
Windowv 上安装Node.js Windows 安装包(.msi) : 32 位安装包下载地址 : http://nodejs.org/dist/v0.10.26/node-v0.10.26-x ...
- [Apple开发者帐户帮助]九、参考(5)支持的功能(tvOS)
tvOS应用程序可用的功能取决于您的程序成员身份. 能力 ADP 企业 Apple开发者 应用程序组 相关域名 背景模式 数据保护 游戏中心 游戏控制器 HomeKit iCloud:Cl ...
- Java使用Cipher类实现加密,包括DES,DES3,AES和RSA加密
一.先看一个简单加密,解密实现 1.1 加密 /** * content: 加密内容 * slatKey: 加密的盐,16位字符串 * vectorKey: 加密的向量,16位字符串 */ publi ...
- [转]sed常用命令总结
转自:http://blog.chinaunix.net/uid-26963748-id-3249732.html 一.Sed简介 Sed:Stream Editor 流式编辑器 又称行编辑器,每次 ...
- 【转】utf-8的中文是一个汉字占三个字节长度
因为看到百度里面这个人回答比较生动,印象比较深刻,所以转过来做个笔记 原文链接 https://zhidao.baidu.com/question/1047887004693001899.html 知 ...
- linux,apache,mysql,php常用查看版本信息的方法
1. 查看linux的内核版本,系统信息,常用的有三种办法: uname -a; cat /proc/version; -bash-4.2$ uname -a Linux apphost -.el7. ...