(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++的多态性必须要用到虚函数,并且还要使用引用或者指针 ...
随机推荐
- linux下非root用户怎样改动root权限的文件
在linux下会出现把一些配置文件參数配错.rootpassword忘记等导致系统无法启动或进入root的窘迫境界.本文以redhat enterprise linux server ...
- Kaggle "Microsoft Malware Classification Challenge"——就是沙箱恶意文件识别,有 Opcode n-gram特征 ASM文件图像纹理特征 还有基于图聚类方法
使用图聚类方法:Malware Classification using Graph Clustering 见 https://github.com/rahulp0491/Malware-Classi ...
- InfluxDB 分布式时间序列数据库环境搭建——据qcon大会2016qiniu说集群很坑且闭源了
InfluxDB 分布式时间序列数据库环境搭建 1. 环境说明 Ubuntu14.04 + influxDB V0.10.1 搭建3个节点的分布式数据库,副本数量2,各节点之间自动进行数据备份并 ...
- C#中Random
说明:C#中的随机数是一个伪随机数,随机数字从一组有限的数字选择以相同的概率,所选的数字不是完全随机的,因为使用数学算法来选择它们.在大多数Windows系统中,Random的15毫秒内创建的对象很可 ...
- mysql中判断记录是否存在方法
以下这个方法是我推荐的. sql语句:select 1 from tablename where col = col limit 1; 然后读取语句执行所影响的行数. 当然这里limit 1很重要.这 ...
- Prime Ring Problem -- 第一个真正意义上的 搜索
这个搜索............搜的我头都大了.......不过还是 懂了那么一点点...哈哈 从3/7晚上 做到3/8晚上------从女生到妇女 我都用来做着一道题了......... 所谓的 ...
- python自动化学习笔记3-集合、函数、模块
文件操作 上次学习到文件的读写,为了高效的读写文件,我们可以用循环的方式,一行一行的进行读写操作,打开文件的方法是open的方法,打开文件执行完后还要进行关闭操作. 一般的文件流操作都包含缓冲机制,w ...
- Android中有四大组件的简单总结
Android四大基本组件分别是Activity,Service服务,Content Provider内容提供者,BroadcastReceiver广播接收器. 一:了解四大基本组件 Activity ...
- Unity Android交互过坑指南
Unity Android交互过坑指南 介于网上看过很多unity和Android交互的教程,都或多或少的漏掉了一些部分,导致编译过程中出现各种问题,特此整理一份教程,仅供参考 介绍 本次实现的是在游 ...
- cocos2dx实现单机版三国杀(二)
接上续,东西还没有做完 所以代码免不了改动 之前的头文件现在又改了不少,因为架构也改变了现在主要类就是GameScene.GameUI.PlayInfo.Poker这四个类 前面想的GameLoop ...