多态

几个相似而不完全相同的的对象,有时人们要求在向他们发出同一个消息时,他们的反应各不相同,分别执行不同的操作,这种情况就是多态现象。

例如windows环境下,鼠标双击一个对象(这就是向对象传递一个消息),如果对象是一个可执行文件,则会执行此程序,如果对象是一个文本文件,则会启动文本编辑器并打开该文件。

C++多态是指,由继承而产生的相关的不同的类,其对相对统一消息会做出不同相应。

 

 

多态实现的前提:赋值兼容。

赋值兼容是指在需要基类对象的任何地方都可以使用公有派生类的对象来替代。赋值兼容是默认行为。

这里的替代包括:

1.派生类对象可以赋值给基类对象。

2.派生类对象可以初始化基类的引用。

3.派生类对象的地址可以赋值给指向基类的指针。

替代后,派生类对象就可以作为基类对象使用,但只能使用从基类继承的成员。

 

补充:父类也可以通过强转的方式转化为子类,这时候他访问从父类继承下来的成员是可以的,但是访问子类成员会发生越界风险。

 

多态形成的条件:

1.静多态(早绑定):

函数重载也是一种多态现象,通过命名倾轧在编译阶段决定,即程序运行之前,在编译阶段就已经确定下来到底要使用哪个函数,

可见:很早就已经将函数编译进去了,称这种情况为早绑定或静态多态

2.动多态(晚绑定)

动态多态是有前提的,它必须以封装和继承为基础

动多态不是在编译器阶段决定,而是在运行阶段决定。

条件如下:a.父类中有虚函数,b.子类override(覆写)父类中的虚函数,c.通过已被子类对象赋值的父类指针或引用,调用共用接口。

3.虚函数

用virtual 修饰成员函数,使其成为 虚函数

在父类中,把想要实现多态的成员函数前加上virtual 关键字,使其成为虚函数。

基类定义虚函数,子类可以重写该函数;在派生类中对基类定义的虚函数进行重写时,需要在派生类中声明该方法为虚方法。

当子类重新定义了父类的虚函数后,当父类的指针指向子类对象的地址时,[即B b; A a = &b;] 父类指针根据赋给它的不同子类指针,动态的调用子类的该函数,而不是父类的函数,且这样的函数调用发生在运行阶段,而不是发生在编译阶段,称为动态联编。而函数的重载可以认为是多态,只不过是静态的。注意,非虚函数静态联编,效率要比虚函数高,但是不具备动态联编能力。

 

如果使用了virtual关键字,程序将根据引用或指针指向的 对 象 类 型 来选择方法,否则使用引用类型或指针类型来选择方法。

 

#include<iostream>
using namespace std;
//基类
class A {
private:
    int i;
public:
    A() {};
    A(int num) 
        :i(num) {};
    virtual void fun1()
    {
        cout << "父类fun1" << endl;
    };   //定义为虚函数
    virtual void fun2()
    {
        cout << "父类fun2" << endl;
    };   //定义为虚函数

};


class B : public A {
private:
    int j;
public:
    B(int num) 
        :j(num){};
    virtual void fun2()
    {
        cout << "派生类fun2" << endl;
    
    };// 重写了基类的方法
};


int main() 
{

        A a(1);
        B b(2);
        A *a1_ptr = &a;
        A *a2_ptr = &b;

        // 当派生类“重写”了基类的虚方法,调用该方法时
        // 程序根据 指针或引用 指向的  “对象的类型”来选择使用哪个方法
        a1_ptr->fun2();// call A::fun2();
        a2_ptr->fun2();// call B::fun1();
               // 否则
               // 程序根据“指针或引用的类型”来选择使用哪个方法
        a1_ptr->fun1();// call A::fun1();
        a2_ptr->fun1();// call A::fun1();
        getchar();
        return 0;
}

 虚函数小结:

1.在基类中使用virual声明成员函数为虚函数。类外实现虚函数时,不必再加virtual。

2.在派生类中重新定义此函数称为 覆写,要求函数名,返回值类型,参数个数以及类型全部匹配。并根据派生类的需要重新定义函数体。

3.当一个成员函数被声明为虚函数时,其派生类中完全相同的函数(显示的写出)也为虚函数。可在其前加上virtual,以示清晰。

4.定义一个指基类对象的指针,并使其指向其子类的对象,通过该指针调用虚函数,此时调用的就是指针变量指向对象的同名函数。

5.子类中的覆写函数,可以为任意访问类型,依子类需求决定。

 

纯虚函数:

在普通的虚函数后面加上"=0"这样就声明了一个纯虚函数

 何时使用纯虚函数?

(1)当想在基类中抽象出一个方法,且该基类只做能被继承,而不能被实例化;
(2)这个方法必须在派生类(derived class)中被实现;
  如果满足以上两点,可以考虑将该方法申明为纯虚函数.

 

 

总结:

1.含有纯虚函数的类,成为抽象基类,不可实例化。即不能创建对象,存在的意义就是被继承,提供族类的共公接口,java中成为interface。

2.纯虚函数只有声明,没有实现,被初始化为0。

3.如果一个类中声明了纯虚函数,而在派生类中没有对该函数定义,则该虚函数在派生类中仍然为纯虚函数,派生类仍然为纯虚基类。

4.含有虚构函数的类,析构函数也应该声明为虚函数,在delete父类指针时,会调用子类的析构函数,实现完整析构。

5.只有类成员函数才能声明为虚函数,虚函数仅适用于有继承关系的类对象,普通函数不能声明为虚函数。

6.静态成员函数不能是虚函数,静态成员函数不受对象捆绑,只有类信息。

7.内联,构造函数不能是虚函数,构造时对象的创建尚未完成。构造完成后,才能算一个对象。

8.析构函数可以是虚函数且通常声明为虚函数。

 

posted @ 2020-03-29 17:05  坦坦荡荡  阅读(238)  评论(0)    收藏  举报