C++基本概念复习之二:多重继承、虚继承、纯虚函数(抽象类)
一、多重继承:
#include <iostream>
using namespace std;
class Horse
{
public:
Horse(){cout<<"Horse constructor...";}
virtual ~Horse(){cout<<"Horse destructor...";}
virtual void whinny()const{cout<<"Whinny!..";}
private:
int itsAge;
};
class Bird
{
public:
Bird(){cout<<"Bird constructor...";}
virtual ~Bird(){cout<<"Bird destructor...";}
virtual void chirp()const{cout<<"Chirp!...";}
virtual void fly(){cout<<"Fly!!!";}
private:
int itsWeight;
};
class Pegasus:public Horse,public Bird
{
public:
Pegasus(){cout<<"Pegasus constructor...";}
//飞马也以鸟鸣的方式叫,但叫出来的声音是马的声音
//注意const不能少,否则就不能覆盖父类中的虚方法
//void chirp()const,从而多态调用失败
//掉了const导致的后果是隐藏,将来通过子类
//对象的指针、引用或者子类对象本身,都无法调用父类的
//同名方法,另一方面,通过指向子类对象的父类类型的指针
//也不能达到动态绑定从而多态调用,调用的永远是父类的
//方法
virtual void chirp()const{whinny();}
virtual ~Pegasus(){cout<<"Pegasu destructor...";}
};
int main()
{
Horse * ranch[2];
Bird * aviary[2];//鸟笼和农场的容量都是2
//观察打印的每一行可以看出每个对象的创建过程
ranch[0]=new Horse;//初始化农场
cout<<endl;
ranch[1]=new Pegasus;
cout<<endl;
aviary[0]=new Bird;//初始化鸟笼
cout<<endl;
aviary[1]=new Pegasus;
cout<<endl;
cout<<"*********************"<<endl;
ranch[0]->whinny();cout<<endl;
ranch[1]->whinny();cout<<endl;
aviary[0]->chirp();aviary[0]->fly();cout<<endl;
aviary[1]->chirp();aviary[1]->fly();cout<<endl;
cout<<"*********************"<<endl;
//观察打印的每一行可以看出每个对象的销毁过程
delete ranch[0];//清理农场
cout<<endl;
delete ranch[1];
cout<<endl;
delete aviary[0];//清理鸟笼
cout<<endl;
delete aviary[1];
cout<<endl;
//上面的子类对象能得以正确析构,要归功于虚析构函数
return 0;
}
打印结果:
避免歧义:
上面的代码中,若Horse和Bird类中都有getColor方法(可能是虚方法,也可能不是),且该方法在两个类中的签名完全一致,对子类来说,这两个方法都继承了,问题也随之而来:
1、当我们在客户端使用子类对象来调用该方法时:
Pegasus pgs;
pgs.getColor();
会告诉错误,因为编译器不知道该调用哪个getColor。解决办法,要么让子类有自己的getColor方法(覆盖父类的getColor),我们可以在子类自己的getColor方法指定调用哪个父类的getColor,或者都不调用,子类的getColor有自己全新的逻辑;要么在调用的时候指定调谁的:pgs.Bird::getColor()。
2、如果我们在客户端用这样调用:
Pegasus pgs;
Horse h=pgs;
h.getColor();
则不会有任何歧义,这种调用方式不会引入多态机制,永远调用父类的方法——与java不同。
3、如果这样调用:
Pegasus pgs;
Horse* ph=&pgs;//或者通过引用
ph->getColor();
也不会有任何歧义,这也不会引入多态机制,仅仅是在调用父类方法。
注意,上面所说的歧义,与getColor是不是虚方法没有关系,即,仅仅靠虚方法不能避免歧义,解决办法与1类似。
另一种歧义:
现在的情况是:马和鸟都继承自动物类Animal,由于飞马从马和鸟多继承,导致飞马对象的内存结构是这样的:
这五块内容,构成一个完整的飞马Pegasu对象,其中有两块一模一样的数据:Animal。这是飞马的两个直接父类从共同的基类Animal继承而来的。代码如下:
#include <iostream>
using namespace std;
enum COLOR{BLACK,RED,BLUE};
class Animal
{
public:
void baseFunc(){cout<<"Animal func..."<<endl;}
};
class Horse:public Animal
{
};
class Bird:public Animal
{
};
class Pegasus:public Horse,public Bird
{
};
int main()
{
Pegasus pgs;
Horse* ph=&pgs;
ph->baseFunc();//无歧义
pgs.baseFunc();//有歧义
return 0;
}
二、虚继承解决歧义:
虚继承得到的最终子类对象的内存模型:
从而能消除上面引起的歧义。代码:
#include <iostream>
using namespace std;
enum COLOR{BLACK,RED,BLUE};
class Animal
{
public:
void baseFunc(){cout<<"Animal func..."<<endl;}
};
class Horse:virtual public Animal
{
};
class Bird:virtual public Animal
{
};
class Pegasus:public Horse,public Bird
{
};
int main()
{
Pegasus pgs;
Horse* ph=&pgs;
ph->baseFunc();//无歧义
pgs.baseFunc();//无歧义,虚继承解决歧义
return 0;
}
虚继承引进的一个注意事项是:共同基类(这里指Animal)的初始化任务,由最终子类完成。——上述代码没有体现这一点,但要注意。所谓的“类的初始化”这种说法,严格来说,并不确切。对于上面的内存模型,它是一个最终子类对象的内存模型,即是一个Pegasu对象,我们给其所占内存里的内容编上号,从上到下从左至右,分别为0,1,2,3:
可以这样理解:整个(0,1,2,3)构成的是一个完整的飞马对象,而0块内容是一个Animal对象,1和0是一个Horse对象,2和0是一个Bird对象。上面所说的“基类的初始化”,意指0块内容的初始化。不涉及虚继承的情况下,父类的初始化,都在直接子类完成——通过在子类构造的参数列表后面手动调用父类相应的构造来完成。
经验总结:在单继承可行的情况,不要使用多继承,避免带来的开销及额外的风险。
三、纯虚函数(抽象类):
有纯虚函数的类是抽象类。抽象类不能实例化,它存在的意义类似java中的接口和抽象类,将后代共同的操作统一声明,至于具体如何实现,后代自己决定。一旦在父类中定义一个纯虚函数,就对子类提出这样的要求:要想使子类不是抽象类,就必须要覆盖实现这个纯虚函数,要覆盖父类中的每个纯虚函数。
虚函数可以有自己的实现。往往它完成的是通用的、基本的工作,通常的做法是:在子类具体的覆盖实现中,调用父类纯虚函数的实现来完成共同的基本操作。其实虚函数也能做到这一点,不过父类必须给出虚函数实现,而不能仅仅是声明。从抽象的角度来讲,具有纯虚函数的类抽象程度更大一些,它仅有通用方法的声明即可,且不能被实例化。
转载于:https://www.cnblogs.com/qzhforthelife/archive/2012/11/04/2753610.html
C++基本概念复习之二:多重继承、虚继承、纯虚函数(抽象类)相关推荐
- 多重继承、虚继承与虚基类
一.多重继承 单重继承--一个派生类最多只能有一个基类 多重继承--一个派生类可以有多个基类 class 类名: 继承方式 基类1,继承方式 基类2,-. {-.}; 派生类同时继承多个基类的成员,更 ...
- C++中的各种“虚“-- 虚函数、纯虚函数、虚继承、虚基类、虚析构、纯虚析构、抽象类讲解
C++中的各种"虚" 1. 菱形继承 1.1 虚继承 && 虚基类 1.2 虚基类指针(vbptr)&& 虚基类表(vbtable) 2. 多态 2 ...
- C++虚继承与虚函数
虚继承和虚函数是完全无相关的两个概念. 虚继承是解决C++多重继承问题的一种手段,从不同途径继承来的同一基类,会在子类中存在多份拷贝.这将存在两个问题:其一,浪费存储空间:第二,存在二义性问题,通常可 ...
- C++虚函数,虚函数表,虚继承,虚继承表
一.虚函数 类中用virtual关键字修饰的函数. 作用:主要是实现了多态的机制.关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数.这种技术可以让父类的 ...
- 【C++】继承和派生、虚继承和虚基类、虚基类表和虚基类指针
继承和派生.虚继承和虚基类.虚基类表和虚基类指针 继承和派生 继承概述 继承基本概念 派生类中的成员 继承的内容 派生类定义 派生类访问控制 对象构造和析构 对象构造和析构的调用顺序 继承中的构造和析 ...
- C++中的虚继承与虚基类
1.Cpp中的虚继承与虚基类 在多继承时,很容易产生命名冲突的问题,即使我们很小心地将所有类中的成员变量和成员函数都命名为不同的名字,命名冲突依然有可能发生,比如典型的是菱形继承,如下图所示: 类A派 ...
- c++虚继承和虚函数和抽象类、接口类,聚合类
虚继承 虚继承用于解决多继承条件下的菱形继承问题(浪费存储空间.存在二义性). 底层实现原理与编译器相关,一般通过虚基类指针和虚基类表实现,每个虚继承的子类都有一个虚基类指针(占用一个指针的存储空间, ...
- 继承、虚继承、虚函数内存分布(MSVC下)
前提知识: 对象的内存中只包含成员变量,存储在栈区或堆区(使用 new 创建对象): 成员函数与对象内存分离,存储在代码区. 对象的大小,可以自己分析,int 四个字节,指针也是四个字节.(在x86中 ...
- C++虚继承和虚基类详解(二)
虚继承(Virtual Inheritance) 为了解决多继承时的命名冲突和冗余数据问题,C++ 提出了虚继承,使得在派生类中只保留一份间接基类的成员. 在继承方式前面加上 virtual 关键字就 ...
最新文章
- java vector search_java.util.Vector.retainAll()方法实例
- 电子科大博士生杨超火了!2年实现Science+Nature一作双杀
- atitit.词法分析的实现token attilax总结
- ELF文件格式详解-请查收
- 关于OC-省市区习题
- php 替换某个字符,php中如何替换字符串中的某个字符-PHP问题
- 不得不说的wepapi 优化
- 未来感十足:小米发布四曲面屏幕专利
- Oracle将Java EE移交Eclipse基金会
- 单片机课设-电子时钟设计(仿真图、代码全)
- 信号与线性系统分析(吴大正,郭宝龙)(3-单位脉冲/阶跃序列以及4-信号的运算)
- 2022年电工(技师)特种作业证考试题库及在线模拟考试
- java孢子进化_孢子的进化起源
- idea2019.2版本gradle 使用offline
- (77)--用框架爬取博客园信息并保存到数据库
- numpy一行转为一列
- python利用selenium和safari浏览器驱动实现新浪微博自动点赞 Demo
- 指数计算机在线使用,ffmi(在线ffmi指数计算器)
- 10.6版本的CodeWarrior 的使用手册
- 联合国发布AI报告:自动化和AI对亚洲有巨大影响
热门文章
- LightOJ 1026 桥 1063 割点
- android 通过webview调起支付宝app支付
- windows64下安装MySQLdb连接数据库
- [导入]Netron研究(二)----容器登场
- Windows消息ID号查看
- C#多线程、并发与并行概念
- 微信小程序:背景图片在电脑可以显示,真机测试时无法显示
- ios开发之使用多文件上传的简单封装最原始的
- matlab 由图片生成视频
- python爬虫技术路线_爬虫学习——中国大学最好排名(技术路线:requests库和bs4)(来源于北理工Python网络爬虫与信息提取网络公开课)...