多态公有继承

多态公有继承:所谓多态公有继承,就是同一个方法在派生类和基类中的行为不同。

  • 实现多态公有继承的机制:
    1,在派生类重新定义基类的方法。(用于对象)
    2,使用虚方法(多用于指针和引用),虚方法在基类用关键字virtual声明,在派生类中会自动识别基类中声明的虚方法。所以,在派生类中可以用(也可以不用)virtual显式标出哪个方法是虚方法。
    通俗来讲:为了实现一种方法可以在派生类和基类中的行为不同,于是在派生类中重新定义基类函数的方法(比如基类有方法A, 但又在派生类中重新定义了方法A, 它们名字一样,但行为不一样),然后通过各自的对象调用各自的方法。但有时候我们会用一个基类类型的引用或者指针来指向派生类的对象,这时如果你使用引用来调用方法,会调用基类的方法A ,还是调用在派生类中重新定义的方法A 呢?答案是:基类的方法A。很简单,因为引用(reference)的类型是基类的。为了能让程序能够根据基类类型的引用(reference)所引用的对象来选择方法,所以定义了虚方法。因此,虚方法是“重新定义基类方法”的扩展,为的是让引用或者指针更加智能地选择对象所对应的方法。
  • 派生类实现:
    1,构造函数使用成员初始化列表语法(参照前一篇);
    2,非构造函数不可以使用成员初始化列表语法,但是派生类方法可以调用公有的基类方法。(对于被重新定义的方法,可以通过使用作用域解释符来调用基类方法,如果在派生类没有重新定义的方法,则直接调用。)
  • 虚析构函数
    一般来数,在基类中总是将析构函数定为虚的。这是为了能让程序更加灵活地调用析构函数。比如:(假设BrassPlus是基类Brass的派生类)

    1 Brass * p_test[2];
    2  p_test[0] = new Brass(x);
    3  p_test[1] = new BrassPlus(y);
    4  delete p_test[0];
    5  delete p_test[1];

    p_test[0] 和 p_test[1] 都是Brass * 类型,如果析构函数不是虚的,那么上面的两个delete都回调用Brass类的析构函数。但p_test[1]是指向派生类的阿,运用基类析构函数消灭派生类对象肯定是不对的。
    如果将基类析构函数声明为虚就不一样了!它会自动为p_test[1] 调用BrassPlus的析构函数!!!是不是有点智能的味道???虚方法的魅力无限!

  • 联编(binding),静态联编(static binding) 和  动态联编(dynamic binding)
    1,联编:将源代码中的函数调用解释为执行特定的函数代码块;在C语言中这很简单,因为每个函数名都对应不同的函数。但在C++中,由于重载,编译器必须要根据函数参数和函数名称才能确定使用哪个版本的代码块;
    2,静态联编:在编译过程中进行联编;
    3,动态联编:编译器必须生成能够在程序运行时选择正确的虚方法的代码。
  • 指针和引用类型的兼容性:
    1,指向基类的引用或指针可以引用派生类对象,而不需要进行显式类新转换,这种将派生类引用或指针转换为基类引用或指针被称为向上强制转换(upcasting)。相反,将基类引用或指针转换为派生类引用或指针,被称为向下强制转换(downcasting)。如果不进行强制类型转换,则向下强制转换是不被允许的,因为is-a关系通常是不可逆的。假设有一个函数:Usetest(Brass & s),那么程序将对它向上强制转换,所以基类对象和派生类BrassPlus对象作为实参传递给Usetest()函数时,Usetest()函数将根据实参的类型来选择调用哪个代码块。需要注意的是,如果形参是Brass,即Usetset(Brass),那么Brass对象和BrassPlus对象都可以作为实参传递给Usetest(),但如果形参是BrassPlus & 时,即Usetest(BrassPlus & ) ,那么只能将BrassPlus对象作为实参传递给Usetest()!!
  • 虚函数的工作原理:
    对于每个类,编译器都创建一个虚函数地址表(virtual function table,vtbl),表中存储了为类对象声明的虚函数地址,这个表的形式是一个数组。当一个对象被创建之时,编译器为这个对象增加了一个隐藏成元,隐藏成员中保存了一个指向虚函数地址表的指针。例如:基类对象包含一个指针,该指针指向基类中所有虚函数的地址表,派生类对象将包含一个指向另一个独立地址表的指针,如果派生类没有重新定义虚函数,那么这两个地址表是一样的,虽然它们存储在不同的位置。如果派生类提供了虚函数的新定义,那么派生类中的虚函数地址表将保存新函数的地址。
    所以使用虚函数时,对于每个函数调用,都需执行一项额外的操作,即到调中查找地址,从而也增加了系统开销。每个对象都将增大,增大两为存储地址(隐藏成员:指针)的空间。
时间: 2024-10-17 16:44:37

多态公有继承的相关文章

C++多态公有继承

面向对象的三个基本特征 面向对象的三个基本特征是:封装.继承.多态.其中,封装可以隐藏实现细节,使得代码模块化:继承可以扩展已存在的代码模块(类):它们的目的都是为了——代码重用.而多态则是为了实现另一个目的——接口重用! 多态: 为了使同一方法在基类和派生类中的实现可以不同. 多态的实现: 1.在派生类中重新定义基类的方法 2.使用虚方法 如果要在派生类中重新定义基类的方法,通常将基类方法声明为虚方法 如果方法是通过引用或指针调用而不是对象直接调用,使用哪一种方法将有两种情况: 1.如果在基类

浅谈C++非多态单继承数据布局

最近在看<深度探索C++对象模型>,打算先总结下C++中的数据布局,这篇暂时先谈谈非多态(non-polymorphic)单继承的情况: 一般而言,当我们谈及C++中的继承和多态就默认进入到其面向对象的语境中了.封装是基础,公有继承(public)是手段,然后带来运行时多态(run-time polymorphism)的弹性,通过“基类指针或引用可以透明的指向任何派生类对象”这句话来支撑着C++的面向对象体系.目的无他,无非是简化用户(这里的用户指的是类的使用者)的使用逻辑,即归一化.归一化说

C++公有继承,私有继承和保护继承的区别

昨天学习三种继承方式,有些比喻十分形象,特此分享. 首先说明几个术语: 1.基类 基类比起它的继承类是个更加抽象的概念,所描述的范围更大.所以可以看到有些抽象类,他们设计出来就是作为基类所存在的(有些名字里面有abstract的). 基类也叫父类,虽然本人觉得这个比喻并不恰当.因为实际上子类是基类的扩充,描述了更加具体的东西.但现实的父子关系并非如此.但在有些方面这个比喻还是比较恰当的. 现在我们假设基类是一个快要退休的富豪.代码如下: class RichMan { public: RichM

七:Java之封装、抽象、多态和继承

本文章介绍了关于Java中的面向对象封装.抽象.继承.多态特点 Java面向对象主要有四大特性:封装.抽象.继承和多态. 一.封装 封装就是将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成"类",其中数据和函数都是类的成员. 在面向对象语言中,封装特性是由类来体现的,我们将现实生活中的一类实体定义成类,其中包括属性和行为(在Java中就是方法),就好像人类,可以具有name,sex,age等属性,同时也具有eat(),sle

C++公有继承

is-a.has-a和like-a.组合.聚合和继承 两组概念的区别 - cbk861110的专栏 - 博客频道 -CSDN.NET http://blog.csdn.net/cbk861110/article/details/9028189 公有继承强调的是is-a的关系,强调的是一种继承关系,而has-a强调的是一种从属关系. 继承的形式为:  class  derived:public  based 公有继承的特点是基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态,而基类的

深入浅出OOP(四): 多态和继承(抽象类)

在本文中,我们讨论OOP中的热点之一:抽象类.抽象类在各个编程语言中概念是一致的,但是C#稍微有些不一样.本文中我们会通过代码来实现抽象类,并一一进行解析. 深入理解OOP(一):多态和继承(初期绑定和编译时多态) 深入理解OOP(二):多态和继承(继承) 深入理解OOP(三):多态和继承(动态绑定和运行时多态) 深入理解OOP(四):多态和继承(C#中的抽象类) 深入理解OOP(五):C#中的访问修饰符(Public/Private/Protected/Internal/Sealed/Cons

深入理解OOP(二):多态和继承(继承)

本文是深入浅出OOP第二篇,主要说说继承的话题. 深入理解OOP(一):多态和继承(初期绑定和编译时多态) 深入理解OOP(二):多态和继承(继承) 深入理解OOP(三):多态和继承(动态绑定和运行时多态) 深入理解OOP(四):多态和继承(C#中的抽象类) 深入理解OOP(五):C#中的访问修饰符(Public/Private/Protected/Internal/Sealed/Constants/Static and Readonly Fields) 深入理解OOP(六):枚举(实用方法)

关于C++中公有继承、私有继承、保护继承的讨论

一.文章来由 简单明了的理由,老生常谈但是没真正解决的问题,想搞清楚这个问题. 二.冗长的定义 我们先来看看这些冗长的定义: 公有继承: 当类的继承方式为公有继承时,基类的公有成员和保护成员的访问属性在派生类中不变,而基类的私有成员不可直接访问.也就是说基类的公有成员和保护成员被继承到派生类中访问属性不变,仍作为派生类的公有成员和保护成员,派生类的其他成员可以直接访问它们.在类族之外只能通过派生类的对象访问从基类继承的公有成员. 私有继承: 当类的继承方式为私有继承时,基类中的公有成员和保护成员

C++中公有继承、保护继承、私有继承的区别

公有继承时基类中各成员属性保持不变,基类中private成员被隐藏.派生类的成员只能访问基类中的public/protected成员,而不能访问private成员:派生类的对象只能访问基类中的public成员. 私有继承时基类中各成员属性均变为private,并且基类中private成员被隐藏.派生类的成员也只能访问基类中的public/protected成员,而不能访问private成员:派生类的对象不能访问基类中的任何的成员. 保护继承时基类中各成员属性均变为protected,并且基类中p