精华内容
下载资源
问答
  • 多态调用和普通调用 C++中只有两个调用关系,分为普通调用和多态调用。不满足多态调用就是普通调用。 多态必须构成的两个条件 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写(也就是必须满足重写...

    多态调用和普通调用

    C++中只有两个调用关系,分为普通调用多态调用。不满足多态调用就是普通调用。

    多态必须构成的两个条件

    1. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写(也就是必须满足重写)
    2. 必须通过基类的指针或者引用调用虚函数(指针和引用)
    多态调用:跟指向的对象有关,传的对象是谁的,调用的就是谁的。
    普通调用:和类型有关。类型是什么调用就是什么,类型是Person的,那么调用的函数就是Person的。 比如下面的例子。

    普通调用的例子:

    class A {};
    class B : public A{};
    class Person{
    public: 
    	virtual B*f(){
    		cout << "Person::f()" << endl;
    		return new B;
    		}
    };
    class Student: public Person{
    public:
    	virtual B* f(){
    		cout << "Student::f()" << endl;
    		return new B;
    		}
    };
    int main ()
    {
    	Person p;
    	p.f();
    	Student s;
    	p = s;       //派生类赋值给子类
    	p.f();
       
        system("pause");
    }
    

    结果
    在这里插入图片描述
    分析
    在这里虽然调用的是虚函数,派生类中也对基类的虚函数进行了重写。但是不是使用基类的指针或引用调用,所以不满足多态调用的条件。
    所以在这里是普通调用。只与类型有关,尽管Student对象s赋值给父类对象p,但是普通调用只与类型有关,所以仍然调用p的f()函数

    多态调用的例子

    class A {};
    class B : public A{};
    class Person{
    public: 
    	virtual B*f(){
    		cout << "Person::f()" << endl;
    		return new B;
    		}
    };
    class Student: public Person{
    public:
    	virtual B* f(){
    		cout << "Student::f()" << endl;
    		return new B;
    		}
    };
    int main()
    {
    	Person p;
    	Student s;
    	Person* ptr = &p;
    	//本来就是Person类的指针,指向Person类的对象,调用的本来就是Person父类的f()函数
    	ptr->f() ; 
    	
    	ptr = &s;
    	ptr->f(); //指向的是子类的对象, 调用的是子类的f()的函数
    }
    

    结果
    在这里插入图片描述
    分析
    在这里被调用的是虚函数,且派生类Student里对虚函数f()进行了重写。又是基类的指针对虚函数的调用,所以满足多态调用。
    ptr是基类的指针,指向的是派生类的对象, 基类的指针调用虚函数,构成多态调用,只和对象有关。所以调用的是派生类的f()函数。

    错误多态调用的例子

    值得注意的一点: 必须是基类的指针对虚函数的调用,下面有一个派生类指针对虚函数的调用

    class A {};
    class B : public A{};
    class Person{
    public:
    	virtual B*f(){
    		cout << "Person::f()" << endl;
    		return new B;
    	}
    };
    class Student : public Person{
    public:
    	virtual B* f(){
    		cout << "Student::f()" << endl;
    		return new B;
    	}
    };
    int main()
    { 
    	Student s;
    	// 派生类指针指向派生类的对象,调用虚构函数,肯定是调用自己的,因为自己完成了对虚函数的重写
    	Student* ps = &s; 
    	ps->f();
    
    	cout << endl;
    
    	Person p;
    	Person* ptr = &p;  // Person类的指针, 指向Person类的对象,调用的本来就是Person类的虚函数
    	ptr->f();
    	cout << endl;
    
    	ps = (Student*)ptr;   //将基类Person的指针强转 赋给派生类Student的指针ps
    	ps->f();         //派生类的指针调用虚函数, 所以不构成重写, 不满足多态调用。 所以和对象没有关系,只与类型有关系
    	system("pause");
    }
    

    结果:
    在这里插入图片描述
    分析:
    在这里看最后一个输出的Person::f()。
    将基类Person的指针强转赋给派生类Student的指针ps。
    派生类的指针调用虚函数, 所以不构成重写, 不满足多态调用。 所以和对象没有关系,只与类型有关。 在这里ptr是Person * 类型的,是强转成Student * 类型的, 所以输出的只与类型有关, 输出Person * 的f(). 所以输出的是Person->f()

    展开全文
  • C#中CLR虚方法的多态调用C#中CLR虚方法的多态调用C#中CLR虚方法的多态调用C#中CLR虚方法的多态调用C#中CLR虚方法的多态调用
  • Java多态调用机制理解

    2017-08-30 15:19:18
    * 运行时, 多态调用的方法用的是子类的方法, 如果子类没有则找父类的. * 成员变量(运行编译看父类): * 编译时, 如果父类没有, 则编译失败. * 运行时, 多态调用的成员变量用的是父类的.再上演示代码: 父类 ...

    首先上难以理解的结论:
    * 成员方法(非静态)(运行看子类, 编译看父类):
    * 编译时, 如果父类没有, 则编译失败.
    * 运行时, 多态调用的方法用的是子类的方法, 如果子类没有则找父类的.
    * 成员变量(运行编译看父类):
    * 编译时, 如果父类没有, 则编译失败.
    * 运行时, 多态调用的成员变量用的是父类的.

    再上演示代码:
    父类

     public class Person {
            @Override
            public String toString(){
                return "I'm a person.";
            }
            public void eat(){
                System.out.println("Person eat");
            }
            public void speak(){
                System.out.println("Person speak");
            }
    
        }
    
    

    子类1: 男孩

    public class Boy extends Person{
            @Override
            public String toString(){
                return "I'm a boy";
            }
            @Override
            public void speak(){
                System.out.println("Boy speak");
            }
            public void fight(){
                System.out.println("Boy fight");
            }
        }

    子类2: 女孩

    public class Girl extends Person{
            @Override
            public String toString(){
                return "I'm a girl";
            }
            @Override
            public void speak(){
                System.out.println("Girl speak");
            }
            public void sing(){
                System.out.println("Girl sing");
            }
        }

    测试代码块

    public static void main(String[] args) {
        Person boy = new Boy();
        Person girl = new Girl();
        System.out.println(boy);  //==> I'm a boy
        boy.eat(); //==>Person eat
        boy.speak(); //==>Boy speak
        //boy.fight();
        System.out.println(girl);  //==>I'm a girl
        girl.eat();  //==>Person eat
        girl.speak(); //==>Girl speak
        //girl.sing();
    }

    运行结果
    Alt text

    理解了上面的代码, 接下来看下上述代码相关的内存模型
    Alt text
    个人觉得这张图非常清晰.

    要深入理解多态, 首先应该有下面相关概念:
    1. 我认为Java能够实现多态的重要原因是有方法的动态绑定. 这也就是为什么成员变量没有多态调用一说, 方法却有. 实现动态绑定主要是基于下面说的方法表.
    2. 方法表, 简单理解就是方法的列表, 每个类都有对应的方法表, 对于父子类同名方法在方法表中具有相同的索引号(专业点说, 就是具有相同签名的方法,在父类、子类的方法表中具有相同的索引号).
    3. 多态调用方法的大致过程: java编译器将java源代码编译成class文件,在编译过程中,会根据静态类型将调用的符号引用写到class文件中。在执行时,JVM根据class文件找到调用方法的符号引用,然后在静态类型的方法表中找到偏移量(我的理解就是索引号),然后根据this指针确定对象的实际类型1,使用实际类型的方法表,偏移量跟静态类型中方法表的偏移量一样,如果在实际类型的方法表中找到该方法,则直接调用,否则,按照继承关系从下往上搜索。

    看到这里应该基本对多态调用的机制有了一个大概的认识了, 我这里主要为了做笔记, 如果想进一步深入了解, 请参考下面的博客:
    http://www.2cto.com/kf/201603/496508.html


    1. this指针起了关键作用, 它相当于使得子类即使披着父类的妆容, 也不忘自身到底是谁.
    展开全文
  • 在java的多态调用中,new的是哪一个类就是调用的哪个类的方法。(错) 解析: java多态有两种情况:重载和重写 在重写中,运用的是动态单分配,是根据new的类型确定对象,从而确定调用的方法; 在重载中,运用...

    在java的多态调用中,new的是哪一个类就是调用的哪个类的方法。(错)

    解析:

    java多态有两种情况:重载和重写

    在重写中,运用的是动态单分配,是根据new的类型确定对象,从而确定调用的方法;

    在重载中,运用的是静态多分派,即根据静态类型确定对象,因此不是根据new的类型确定调用的方法

    参考链接:https://www.nowcoder.com/test/question/done?tid=32442497&qid=14992#summary

    展开全文
  • 动态多态以及多态调用过程

    千次阅读 2017-02-26 15:07:22
     静态多态是指编译器在编译期间完成的,编译器根据函数实参的类型(可能会进行隐式类型转换),可推断出要调用的那个函数,如果有对应的函数就调用该函数,否则会出现编译错误。  动态多态,我们在这里主要说明的...

            多态分为静态多态与动态多态。静态多态包括函数重载,泛型编程。动态是虚函数的使用。

            静态多态是指编译器在编译期间完成的,编译器根据函数实参的类型(可能会进行隐式类型转换),可推断出要调用的那个函数,如果有对应的函数就调用该函数,否则会出现编译错误。

            动态多态,我们在这里主要说明的是动态多态。

            动态绑定:在程序执行期间(非编译器)判断所引用对象的实际类型,根据其实际类型调用相应的方法。使用virtual关键字修饰类的成员函数时,指明该函数为虚函数,派生类需要重新实现,编译器将实现动态绑定。动态绑定的条件是基类中的函数必须是虚函数,且派生类一定要重写基类的虚函数;第二点是通过基类类型的引用或者指针调用虚函数。

            纯虚函数:在成员函数的形参列表后面写上"=0",则成员函数为纯虚函数,包含纯虚函数的类叫做抽象类,也叫接口类,抽象类不能实例化出对象,纯虚函数在派生类中重新定义以后,派生类才能实例化出对象。

             eg: virtual void Display() = 0;

           继承体系同名成员函数的关系

            重载:在同一作用域,函数名相同,参数不同,返回值可以不同。

            重写(覆盖):函数名相同,参数相同,返回值相同(协变除外);基类函数必须有virtual关键字;访问修饰符可以不同。

            重定义(隐藏):在不同的作用域中(分别在基类和派生类);函数名相同;在基类和派生类中只要不构成重写就是重定义。

            总结(以下是有关动态多态的总结)

            1.派生类重写基类的虚函数实现多态,要求函数名,参数列表,返回值完全相同(协变除外)。

            2.基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性。

            3.只有类的非静态成员函数才能定义是虚函数,静态成员函数不能定义为虚函数。

            4.如果在类外定义虚函数,只能在声明函数时加virtual关键字,定义时不用加。

            5.构造函数不能定义为虚函数,虽然可以将operator=定义为虚函数,但最好不要这么做,使用时容易混淆。

            6.不要再构造函数和析构中调用虚函数,在析构函数和构造函数中对象是不完整的,可能会出现未定义的行为。

            7.最好将基类的析构函数声明为虚函数。

            8.虚表是所有类对象实例共用的。

            多态实现原理

            带有虚函数的类比正常的类多了四个字节,里面保存了一个指针。如下图所示:

          

            派生类虚函数表的创建过程是要创建一个派生类的对象先要调用基类的构造函数,此时虚表指针为基类的虚表指针,基类构造函数调用完成后,再把基类的虚表指针替换为派生类的虚表指针,从而创建派生类对象。在派生类构建虚函数表时,可以看成将基类的虚函数拷贝了一份,若在派生类中虚函数重写,则基类的虚函数被派生类中重写的虚函数所替换掉,若没有构成重写,将不会被覆盖,且没有重写的虚函数在派生类虚函数表中的位置不变,若派生类中存在新的虚函数,则按照定义顺序跟在已经被覆盖的基类虚函数表的后面,此时派生类的虚函数创建成功。

            代码实现:

     

    class Base
    {
    	virtual void Funtest1()
    	{
    		cout << "Base::Funtest1()" << endl;
    	}
    	virtual void Funtest2()
    	{
    		cout << "Base::Funtest2()" << endl;
    	}
    	int _b;
    };
    class Derived:public Base
    {
    	void Funtest1()
    	{
    		cout << "Derived::Funtest1()" << endl;
    	}
    	void Funtest2()
    	{
    		cout << "Derived::Funtest2()" << endl;
    	}
    	void Funtest3()
    	{
    		cout << "Derived::Funtest3()" << endl;
    	}
    	virtual void Funtest4()
    	{
    		cout << "Derived::Funtest4()" << endl;
    	}
    	int _d;
    };
    
    typedef void (*Fun)();
    
    void Printvpf(Base& b)
    {
    	int* addr = (int*)*((int*)&b);
    	Fun* pFun = (Fun*)addr;
    	while (*pFun)
    	{
    		(*pFun)();
    		pFun = (Fun*)++addr;
    	}
    }
    
    int main()
    {
    	Base b;
    	Derived d;
    	Printvpf(b);
    	Printvpf(d);
    	return 0;
    }

            如下图所示:

            

             普通虚拟继承的对象模型:

                  

             在这里,若派生类中没有自己新加的虚函数,则不会创建第二个虚表指针。

             多继承的虚拟继承与上面的普通虚拟继承对象模型类似,如果派生类中新加了虚函数,则新加的虚函数会放在第一个继承基类虚表的后面。

             菱形虚拟继承:

    class B
    {
    public :
        virtual void FunTest1()
        {
            cout<<"B::FunTest1()"<<endl;
        }
        int _b;
    };
    class C1:virtual public  B
    {
    public :
        virtual void FunTest1()//重写B
        {
            cout<<"C1::FunTest1()"<<endl;
        }
        virtual void FunTest2()//新加的虚函数
        {
            cout<<"C1::FunTest2()"<<endl;
        }
        int _c1;
    };
    class C2:virtual public B
    {
    public :
        virtual void FunTest1()//重写B中虚函数
        {
            cout<<"C2::FunTest1()"<<endl;
        }
        virtual void FunTest3()//新加虚函数
        {
            cout<<"C2::FunTest3()"<<endl;
        }
        int _c2;
    };
    class D:public C1,public C2
    {
    public :
        virtual void FunTest1()//重写C1和C2中的虚函数
        {
            cout<<"D::FunTest1()"<<endl;
        }
        virtual void FunTest2()//重写C1中的虚函数
        {
            cout<<"D::FunTest2()"<<endl;
        }
        virtual void FunTest3()//重写C2中的虚函数
        {
            cout<<"D::FunTest3()"<<endl;
        }
        int _d;
    };
    void Test1()
    {
        D d;
        d._b = 1;
        d._c1 = 2;
        d._c2 = 3;
        d._d = 4;
    }
    int main()
    {
        Test1();
        return 0;
    }


                  

             上图则为菱形虚拟继承的对象模型。

              

              

    展开全文
  • 多个对象在访问或修改static修饰的成员变量时,其中一个对象将static成员变量值进行了修改,其他对象中的static成员变量值跟着改变,即多个对象共享同一个static成员变量)多态是指父类引用指向子类对象,调用方法时...
  • 多态调用: 会调用子类中重写的方法 多态的实现效果: 配合方法的重写,当父类引用指向不同的子类对象,同一个方法具有不同的实现方式-->行为多态 多态调用成员的特点: 父类引用调用 成员变...
  • 多态调用顺序

    2010-11-04 18:06:38
    方法调用的优先问题 ,优先级由高到低依次为: this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)
  • 2、多态 (1)对狗进行描述(子级) public class Dog extends Animals { //创建一个"eat"方法 public void eat( ) { System.out.println("狗吃骨头"); } //创建一个"...
  • 其次,若基类和派生类都具有相同的东西,那么在派生类的虚表中,我派生类要保持我自己的特点,所以此时派生类的虚表中存放的是自己的虚函数,这样做的目的很简单,就是为了在多态调用时,会很灵活,根据对象本身自己...
  • 这篇文章主要探究了Java运行时多态中,子类重写了父类方法,并定义了和父类相同名称的成员变量,调用父类的方法,成员变量、静态变量等属性时,会具体调用子类还是父类的属性 先写一段Java多态的代码 父类 public ...
  • 子类和父类之间存在多态时,我们来探讨一下编译和运行时候调用的特点: 先上代码: class Fu { int num = 3; void show() { System.out.println("fu show"); } static void method() { System.out....
  • 方法重写(覆写/覆盖):出现在具有继承关系的类中,方法参数类型和个数、方法名以及返回值都相同,子类方法的访问控制权限不能小于父类方法,子类方法会覆盖掉父类方法,如果想调用父类的同名方法,可以使用super...
  • java多态有两种情况:重载和覆写 在覆写中,运用的是动态单分配,是根据new的类型确定对象,从而确定调用的方法; 在重载中,运用的是静态多分派,即根据静态类型确定对象,因此不是根据new的类型确定调用的...
  • 2.1多态的方法是可以重写的,但其定义的变量是不能重写的,就是保持原值。记忆方式:孔子教书,孔子本人与其爸均在教书上,但二者年龄不能重写 2.2向下转型与向上转型 calss Animal{ void eat(); } class ...
  • 多态方法调用问题

    2019-09-24 18:11:47
    多态注意点: * 1:多态情况下,子类中存在跟父类同名的成员变量时,访问的是父类的成员变量 * 2: 多态情况下,子类中存在跟父类同名的非静态方法时,调用的是子类中的方法 * 3: 多态情况下,子类中存在跟父类同名的...
  • 多态中对成员的调用

    2016-08-14 14:08:41
    多态调用时,只看调用该成员变量的引用所属的类中的成员变量。简单说:无论编译或者运行,都看等号左边就哦了。2.成员方法。 出现一模一样方法时, 多态调用。 编译时,看的是引用变量所属的类中的方法。 运行时,看...
  • 关于多态调用子类特有方法 当使用多态方式调用方法时,首先检查父类中是否有该方法,如果有,再去调用子类的同名方法;如果没有,,只能向下转型 import javax.print.attribute.standard.RequestingUserName; ...
  • java 多态函数调用

    2018-07-03 10:59:19
    子类覆盖父类的函数,以父类身份运行时,实际调用的是子类的函数
  • 多态调用原理 什么是多态? 通俗的,一个事物有多种状态,在C++里是指一个基类成员函数被不同的派生类或者基类调用,有不同的结果。用基类的指针或引用操纵多个类型的能力被称为多态 多态分为静态多态...
  • Java的多态调用

    2018-05-17 16:01:02
    public class Index{ public static void main (String[]args){ Questions questions = new Answer1(); questions.getSex();...}多态是什么多态是指两个或多个属于不同类的对象,对于同一个消息(方法...
  • 很多人说对象有 指向方法区的指针。(应该说的是对象头部的Markword中有指向这对象的Class指针)但是如果子类继承一个父类 比如 Father f = new Son(), 这个引用是怎么找到 父类中方法信息的。...
  • 多态

    2019-07-19 23:57:44
    体现:父类引用变量可以指向子类对象 前提:必须有子父类关系或者类实现接口关系,否则无法...当子父类中出现同名的成员变量时,多态调用该变量时: 编译时期:参考的是引用型变量所属的类中是否有被调用的成员...
  • 多态的方法调用

    2018-10-12 09:52:08
    #2.b.method之所以会调用子类的方法,注意重写的含义是“覆盖”,通过new出来的子类对象调用的方法就是子类重写的方法,所以会打印3. 第二题: public class Test2 { public static void main(String[] args) {...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 231,097
精华内容 92,438
关键字:

多态调用