精华内容
下载资源
问答
  • C++多态概念意义

    千次阅读 2020-09-04 10:12:31
    2、C++如何支持多态概念 3、多态内部运行剖析 4、多态的意义 5、修改示例代码 四、静态联编和动态联编 五、小结 一、什么叫重写 1、子类中可以重定义父类中已经存在的成员函数; 2、这种重定义发生在继承中...

    目录

    一、什么叫重写

    二、面向对象期望的重写

    1、示例分析

    2、所期望的重写

    三、多态的概念和意义

    1、多态的概念

    2、C++如何支持多态概念

    3、多态内部运行剖析

    4、多态的意义

    5、修改示例代码

    四、静态联编和动态联编

    五、小结


    一、什么叫重写

    1、子类中可以重定义父类中已经存在的成员函数;

    2、这种重定义发生在继承中,叫做函数重写;

    3、函数重写是同名覆盖的一种特殊情况。

    二、面向对象期望的重写

    先来验证个小示例:

    #include <QCoreApplication>
    #include <iostream>
    #include <QDebug>
    
    using namespace std;
    
    class Parent
    {
    
    public:
    
        void Printfln()
        {
            std::cout << "Parent: void Printfln()..." << std::endl;
        }
    };
    
    class Child : public Parent
    {
    public:
    
        void Printfln()
        {
            std::cout << "Child: void Printfln()..." << std::endl;
        }
    };
    
    void run(Parent *p)
    {
        p->Printfln();
    }
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
    
        Parent p;
        Child c;
    
        run(&p);
        run(&c);
    
        return a.exec();
    }
    

    1、示例分析

    1、结果:结果是调用两次run函数打印的结果相同;

    2、分析:

    (1)编译期间,编译器只能根据指针的类型判断所指向的对象;

    (2)根据赋值兼容性原则,编译器认为父类指针指向的是父类对象;

    (3)run函数中的指针p,编译器不可能知道它指向的究竟是什么,但是编译器没理由报错,编译器认为最安全的做法就是调用父类Printfln()函数,因为父类和子类都有相同的Printfln()函数;

    (4)因此,编译器结果只可能是调用父类定义的同名函数。

    2、所期望的重写

    面向对象的期望重写行为:根据实际的对象类型来判断如何调用重写函数

    (1)当父类指针(引用)指向父类对象,则调用父类中定义的成员函数;

    (2)当父类指针(引用)指向子类对象,则调用子类中定义的重写函数。

    三、多态的概念和意义

    1、多态的概念

    (1)根据实际的对象类型决定函数调用的具体目标;

    (2)同样的调用语句在实际运行时有多种不同的表现形态体现,这就是多态,同一语句具有多种形态。

    2、C++如何支持多态概念

    (1)通过使用virtual关键字对多态进行支持;

    (2)被virtual修饰的函数被重写后具有多种不同的表现形态;

    (3)被virtual声明的函数称为虚函数。

    3、多态内部运行剖析

    父类和子类的对象,首地址都各自有一个虚函数表地址,该地址都保存着虚函数Printfln()的地址,在运行过程中

    (1)在父类指针(引用)p,指向父类对象,就相当于指向父类的虚函数表,从而在父类的虚函数表中找到Printfln()函数;

    (2)在父类指针(引用)p,指向子类对象,相当于指向子类的虚函数表,从而在子类的虚函数表中找到Printfln()函数。

    4、多态的意义

    (1)在程序运行中展现出动态的特性;

    (2)函数重写必须多态实现,否则没有任何意义;

    (3)多态是面向对象组件化程序设计的基础特性。

    5、修改示例代码

    根据多态的概念,在程序中给父子类同名函数声明virtual关键字,只需要在父类中声明即可,在子类中可以不写,写了也是对的,编译器不会报错。

    #include <QCoreApplication>
    #include <iostream>
    #include <QDebug>
    
    using namespace std;
    
    class Parent
    {
    
    public:
    
        virtual void Printfln()
        {
            std::cout << "Parent: void Printfln()..." << std::endl;
        }
    };
    
    class Child : public Parent
    {
    public:
    
        void Printfln()
        {
            std::cout << "Child: void Printfln()..." << std::endl;
        }
    };
    
    void run(Parent *p)
    {
        p->Printfln();
    }
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
    
        Parent p;
        Child c;
    
        run(&p);
        run(&c);
    
        return a.exec();
    }
    

    四、静态联编和动态联编

    1、静态联编:程序编译期间就能确定的具体的函数调用,如函数重载;

    2、动态联编:程序运行期间才能确定的具体的函数调用,如函数重写。

    五、小结

    1、函数重写只能发生在父类和子类之间;

    2、多态是根据实际对象类型确定调用具体的函数;

    3、virtual关键字是C++支持多态的唯一方式;

    4、被重写的虚函数具有多种不同表现形态。

    展开全文
  • 一、概念 多态就是指同一种事物表现出来的多种形态。 例如: 整数: byte b = 10; short s = 10; int i = 10; c++的多态可以分为两种:编译多态、运行时多态。而Java中的多态只有一种,就是运行时多态,是一种...

    #多态

    一、概念

    多态就是指同一种事物表现出来的多种形态。

    例如:

    整数: byte b = 10; short s = 10; int i = 10;

    c++的多态可以分为两种:编译多态、运行时多态。而Java中的多态只有一种,就是运行时多态,是一种运行期间的行为,而不是编译期的行为。

    二、语法格式

    父类类型  引用   =  new 子类类型();

    例如:

    Person p = new Student();

    p.show();

    解析:在编译阶段p是Person类型,因此调用Person类自己的show()方法,若没有则编译的时候就会报错。

               在运行阶段p真正指向的对象是Student类型,因此最终调用的是Student类中自己的show()方法。

    注:

    (1)当使用多态方式调用方法的时候,首先会检查父类中是否有该方法,如果没有,则编译报错。

    (2)如果有,再去调用子类的同名方法。(注意:静态static方法属于特殊情况,静态方法只能继承,不能重写Override,如果子类中定义了同名同形式的静态方法,它对父类方法只能起到隐藏的作用。)

    三、多态存在的三个比较条件:(1)要有继承(2)要有重写(3)父类引用指向子类对象

    四、多态的效果:

    (1)对于指向子类对象的父类来说,在编译期间只能调用父类的方法,不能直接调用子类的方法。

    (2)对于父子类都有的非静态方法来说,最终调用子类中的重写版本。

    (3)对于父子类中都有的静态方法来说,最终调用父类中的版本,与指向的对象类型无关。

    五、引用类型之间的转换

    (1)引用类型之间的转换必须发生在父子类之间,分为自动类型转换(向上转换)和强制类型转换(向下转换)

    (2)向上转换(子类类型向父类类型转换)

    父类引用指向子类对象:Person p = new Student();

    把一个子类对象转换为父类对象,向上转型(自动转型、隐式转型),代码中是父类引用指向子类的对象,父类引用指向类型实例,可以调用子类重写父类的方法以及父类派生的方法,无法调用子类独有的方法。

    注:父类中的静态方法无法被子类重写,所以向上转型之后只能调用父类原有的静态方法。

    (3)向下转换(父类类型向子类类型的转换)

    子类引用指向父类实例,必须进行强制类型转换,可以调用子类特有的方法。

    (4)当没有父子类关系发生强制类型转换则编译报错,当拥有父子类关系发生强制转换时编译通过。

             若目标类型并不是该引用真正指向的类型时,在运行阶段发生类型转换异常。

    (5)为了避免上述情况发生,通常只要进行强制类型转换就需要进行判断,格式如下:

    if(引用变量名  instanceof 数据类型){ }

    - 判断引用指向的对象是否为指定的数据类型,若是则返回true,否则返回false。

     

    下面用一个案例来了解多态的实际意义:

    形状:横坐标、纵坐标

    矩形:长度、宽度

    圆形:半径

    要求:写一个方法既能打印矩形又能打印圆形。

    首先是形状类:封装,get、set方法,无参、有参构造

    然后是矩形类:同样的封装,get、set方法,无参、有参构造,同时重写show方法

    接着是圆形类:同样的封装,get、set方法,无参、有参构造,同时重写show方法

    最后是测试类及控制台输出结果:注释掉的方法是分别调用矩形类和圆形类自己的show()方法,需要接收的对象数量比较少时可以这样来做,也能顺利完成,但接收对象数量十分庞大时,就需要用到多态了。通过编写这样一个案例,我们可以轻易看出合理运用多态可以提高效率,这也是多态的实际意义所在。

     

    展开全文
  • 在面向对象编程(Object-Oriented Programming, OOP)中,多态机制无疑是其最具特色的功能,甚至可以说,不运用多态的编程不能称之为OOP。这也是为什么有人说,使用面向对象语言的编程和面向对象的编程是两码事。 ...

    在面向对象编程(Object-Oriented Programming, OOP)中,多态机制无疑是其最具特色的功能,甚至可以说,不运用多态的编程不能称之为OOP。这也是为什么有人说,使用面向对象语言的编程和面向对象的编程是两码事。

    多态并没有一个严格的定义,维基百科上给它下的定义比较宽松:

    Subtype polymorphism, almost universally called just polymorphism in the context of object-oriented programming, is the ability of one type, A, to appear as and be used like another type, B.

    一、子类型和子类 
    这里我想先提一下子类型(Subtype)这个词和子类(Subclass)的区别,简单地说,只要是A类运用了extends关键字实现了对B类的继承,那么我们就可以说Class A是Class B的子类,子类是一个语法层面上的词,只要满足继承的语法,就存在子类关系。

    子类型比子类有更严格的要求,它不仅要求有继承的语法,同时要求如果存在子类对父类方法的改写(override),那么改写的内容必须符合父类原本的语义,其被调用后的作用应该和父类实现的效果方向一致。

    对二者的对比是想强调一点:只有保证子类都是子类型,多态才有意义。

    二、多态的机制 
    本质上多态分两种:

    1、编译时多态(又称静态多态)

    2、运行时多态(又称动态多态)

    重载(overload)就是编译时多态的一个例子,编译时多态在编译时就已经确定,运行时运行的时候调用的是确定的方法。

    我们通常所说的多态指的都是运行时多态,也就是编译时不确定究竟调用哪个具体方法,一直延迟到运行时才能确定。这也是为什么有时候多态方法又被称为延迟方法的原因。

    在维基百科中多态的行为被描述为:

    The primary usage of polymorphism in industry (object-oriented programming theory) is the ability of objects belonging to different types to respond to method, field, or property calls of the same name, each one according to an appropriate type-specific behavior.

    下面简要介绍一下运行时多态(以下简称多态)的机制。

    多态通常有两种实现方法:

    1、子类继承父类(extends)

    2、类实现接口(implements)

    无论是哪种方法,其核心之处就在于对父类方法的改写或对接口方法的实现,以取得在运行时不同的执行效果。

    要使用多态,在声明对象时就应该遵循一条法则:声明的总是父类类型或接口类型,创建的是实际类型。举例来说,假设我们要创建一个ArrayList对象,声明就应该采用这样的语句:

    List list = new ArrayList(); 
    而不是

    ArrayList list = new ArrayList(); 
    在定义方法参数时也通常总是应该优先使用父类类型或接口类型,例如某方法应该写成:

    public void doSomething(List list); 
    而不是

    public void doSomething(ArrayList list); 
    这样声明最大的好处在于结构的灵活性:假如某一天我认为ArrayList的特性无法满足我的要求,我希望能够用LinkedList来代替它,那么只需要在对象创建的地方把new ArrayList()改为new LinkedList即可,其它代码一概不用改动。

    The programmer (and the program) does not have to know the exact type of the object in advance, and so the exact behavior is determined at run-time (this is called late binding or dynamic binding).

    虚拟机会在执行程序时动态调用实际类的方法,它会通过一种名为动态绑定(又称延迟绑定)的机制自动实现,这个过程对程序员来说是透明的。

    三、多态的用途 
    多态最大的用途我认为在于对设计和架构的复用,更进一步来说,《设计模式》中提倡的针对接口编程而不是针对实现编程就是充分利用多态的典型例子。定义功能和组件时定义接口,实现可以留到之后的流程中。同时一个接口可以有多个实现,甚至于完全可以在一个设计中同时使用一个接口的多种实现(例如针对ArrayList和LinkedList不同的特性决定究竟采用哪种实现)。

    四、多态的实现 
    下面从虚拟机运行时的角度来简要介绍多态的实现原理,这里以Java虚拟机(Java Virtual Machine, JVM)规范的实现为例。

    在JVM执行Java字节码时,类型信息被存放在方法区中,通常为了优化对象调用方法的速度,方法区的类型信息中增加一个指针,该指针指向一张记录该类方法入口的表(称为方法表),表中的每一项都是指向相应方法的指针。

    方法表的构造如下:

    由于Java的单继承机制,一个类只能继承一个父类,而所有的类又都继承自Object类。方法表中最先存放的是Object类的方法,接下来是该类的父类的方法,最后是该类本身的方法。这里关键的地方在于,如果子类改写了父类的方法,那么子类和父类的那些同名方法共享一个方法表项,都被认作是父类的方法。

    注意这里只有非私有的实例方法才会出现,并且静态方法也不会出现在这里,原因很容易理解:静态方法跟对象无关,可以将方法地址直接引用,而不像实例方法需要间接引用。

    更深入地讲,静态方法是由虚拟机指令invokestatic调用的,私有方法和构造函数则是由invokespecial指令调用,只有被invokevirtual和invokeinterface指令调用的方法才会在方法表中出现。

    由于以上方法的排列特性(Object——父类——子类),使得方法表的偏移量总是固定的。例如,对于任何类来说,其方法表中equals方法的偏移量总是一个定值,所有继承某父类的子类的方法表中,其父类所定义的方法的偏移量也总是一个定值。

    前面说过,方法表中的表项都是指向该类对应方法的指针,这里就开始了多态的实现:

    假设Class A是Class B的子类,并且A改写了B的方法method(),那么在B的方法表中,method方法的指针指向的就是B的method方法入口。

    而对于A来说,它的方法表中的method方法则会指向其自身的method方法而非其父类的(这在类加载器载入该类时已经保证,同时JVM会保证总是能从对象引用指向正确的类型信息)。

    结合方法指针偏移量是固定的以及指针总是指向实际类的方法域,我们不难发现多态的机制就在这里:

    在调用方法时,实际上必须首先完成实例方法的符号引用解析,结果是该符号引用被解析为方法表的偏移量。虚拟机通过对象引用得到方法区中类型信息的入口,查询类的方法表,当将子类对象声明为父类类型时,形式上调用的是父类方法,此时虚拟机会从实际类的方法表(虽然声明的是父类,但是实际上这里的类型信息中存放的是子类的信息)中查找该方法名对应的指针(这里用“查找”实际上是不合适的,前面提到过,方法的偏移量是固定的,所以只需根据偏移量就能获得指针),进而就能指向实际类的方法了。

    我们的故事还没有结束,事实上上面的过程仅仅是利用继承实现多态的内部机制,多态的另外一种实现方式:实现接口相比而言就更加复杂,原因在于,Java的单继承保证了类的线性关系,而接口可以同时实现多个,这样光凭偏移量就很难准确获得方法的指针。所以在JVM中,多态的实例方法调用实际上有两种指令:

    invokevirtual指令用于调用声明为类的方法;

    invokeinterface指令用于调用声明为接口的方法。

    当使用invokeinterface指令调用方法时,就不能采用固定偏移量的办法,只能老老实实挨个找了(当然实际实现并不一定如此,JVM规范并没有规定究竟如何实现这种查找,不同的JVM实现可以有不同的优化算法来提高搜索效率)。我们不难看出,在性能上,调用接口引用的方法通常总是比调用类的引用的方法要慢。这也告诉我们,在类和接口之间优先选择接口作为设计并不总是正确的,当然设计问题不在本文探讨的范围之内,但显然具体问题具体分析仍然不失为更好的选择。

    展开全文
  • 多态的基本概念 多态:同一种事物表现出来的多种形态。 多态语法格式 格式为:父类类型的引用指向子类类型的对象。如下所示 Person person=new Worker(); 多态的特点 (1)当父类类型的引用指向子类类型的对象时,...

    多态的基本概念

    多态:同一种事物表现出来的多种形态。

    多态语法格式

    格式为:父类类型的引用指向子类类型的对象。如下所示

    Person person=new Worker();
    

    多态的特点

    (1)当父类类型的引用指向子类类型的对象时,可以直接访问父类独有的方法。
    (2)当父类类型的引用指向子类类型的对象时,无法直接访问子类独有的方法,但可以通过强制类型转换访问。
    (3)对于父子类都有的非静态方法,编译阶段调用父类的方法,运行阶段调用子类的方法(动态绑定)。
    (4)对于父子类都有的静态方法,编译和运行阶段都调用父类的静态方法。

    引用数据类型的转换

    引用数据类型之间的转换有两种,一是自动类型转换,二是强制类型转换。自动类型转换是指由小转大,由子类转换为父类,也称作向上转换。强制类型转换是指由大转小,由父类转换为子类,也称作向下转换或者显示转换(因需加上需转换的类型)。如下所示

    Worker w=new Worker();
    Person p=w;//自动类型的转换
    
    Person p=new Worker();
    ((Worker)p).work();//强制类型转换,work()是Worker独有的方法
    

    强转的注意事项

    (1)当引用变量强转为非指向对象类型的对象时,编译通过,运行时会出现类型转换异常(ClassCastException)。例如:

    Person p=new Worker();//Worker,Dancer继承自Person类
    ((Dancer)p).dance();//强转为非指向对象的类型,出现异常
    

    (2)为了避免该错误的放生,进行转换前可通过 if(引用变量 instanceof 数据类型)判断能否进行强转。例如:

    if(p instanceof Dancer)
    	((Dancer)p).dance();
    else
    	System.out.Println("类型错误,无法进行强转");
    

    多态的实际意义

    意义:多态的意义在于屏蔽不同子类的差异性实现通用的编程带来不同的效果。例如,通过多态实现一个方法可将参数对象的特征打印出来,其中参数对象可以是不同的子类对象。
    先定义Worker,Dancer类继承自Person类

    public  class Person {
        public  void speak(){
            System.out.println("我是Person");
        }
    }
    
    public class Worker extends Person{
        @Override
        public void speak() {//重写父类方法
            System.out.println("我是一个Worker");
        }
    }
    
    public class Dancer extends Person{
        @Override
        public void speak() {//重写父类方法
            System.out.println("我是一个Dancer");
        }
    }
    
    

    最后是测试类

    public class PersonTest {
        public static void speak(Person p){
            p.speak();
        }
        public static void main(String[] args) {
            //将不同子类对象作为形参
            PersonTest.speak(new Worker());
            PersonTest.speak(new Dancer());
        }
    }
    

    运行结果如下
    在这里插入图片描述

    展开全文
  • 文章目录1 C++中多态概念意义1.1 函数重写回顾1.2 多态概念1.3 多态意义1.4 静态联编和动态联编 1 C++中多态概念意义 1.1 函数重写回顾 对于函数重写: 父类被重写的函数依然会继承给子类。 子类中重写...
  • 多态概念意义

    2020-03-14 20:57:54
    在之前同名覆盖引发问题的那一...指向子类对象则调用子类中定义的重写函数,不要根据指针类型去调用,要根据指针指向的对象类型去调用,现在我们引入多态概念 要实现多态,就需要用到virtual关键字对多态进行支持,被...
  • 多态的使用意义

    2017-07-31 16:06:00
    1.多态(Polymorphism):就是父类型的引用能够指向子类型的对象。 2.Parent p = new Child ();当使用多态调用方法时。首先检查父类有没有该方法,若有,则运行该方法;若没有,则编译错误。 3.一共同拥有两种类型的...
  • 49-多态概念意义

    2017-10-30 18:03:32
    1、函数重写(override)回顾2、多态概念意义3、4、#include #include <string>using namespace std;class Parent { public: virtual void print() { cout ; } };class Child
  • 1、函数重写回顾 父类中被重写的函数依然会继承给子类 子类中重写的函数将覆盖父类中的函数 通过作用域分辨符(::)可以访问到父类中的函数 Child c; Parent* p = &...2、多态概念和...
  • 多态概念意义 面向对象中期望的行为: 根据实际的对象类型判断如何调用重写函数 父类指针(引用)指向 父类对象则调用父类中定义的函数 子类对象则调用子类中定义的重写函数 面向对象中的多态概念 根据...
  • 2.必须要有方法的重写(否则多态会没有意义) 3.(核心)父类的引用 指向 子类的空间(父类的指针 指向 子类对象) 代码例子: public class Kll { public static void main(String[] args) { Animal...
  • 8、多态概念意义

    2020-08-05 19:00:02
    一、什么是多态? 1、多态初认识 多态也就是面向对象中期望的行为。 (1) 根据实际的对象类型判断如何调用重写函数 (2) 父类指针(引用)指向 父类对象则调用父类中调用的函数 子类对象则调用子类中定义的重写...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 45,035
精华内容 18,014
关键字:

多态的概念及意义