多态公有继承

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

  • 实现多态公有继承的机制:
    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),表中存储了为类对象声明的虚函数地址,这个表的形式是一个数组。当一个对象被创建之时,编译器为这个对象增加了一个隐藏成元,隐藏成员中保存了一个指向虚函数地址表的指针。例如:基类对象包含一个指针,该指针指向基类中所有虚函数的地址表,派生类对象将包含一个指向另一个独立地址表的指针,如果派生类没有重新定义虚函数,那么这两个地址表是一样的,虽然它们存储在不同的位置。如果派生类提供了虚函数的新定义,那么派生类中的虚函数地址表将保存新函数的地址。
    所以使用虚函数时,对于每个函数调用,都需执行一项额外的操作,即到调中查找地址,从而也增加了系统开销。每个对象都将增大,增大两为存储地址(隐藏成员:指针)的空间。
原文地址:https://www.cnblogs.com/busui/p/5801146.html