精华内容
下载资源
问答
  • 动态多态以及多态调用过程

    千次阅读 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;
    }


                  

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

              

              

    展开全文
  • C++ 多态

    2019-07-09 02:32:49
    多态包括: 编译时多态:函数重载就是一个例子。静态多态是编译器在编译期间完成的,编译器会根据实参类型来选择调用合适的函数,如果有合适的函数可以调用就调,没有的话就会发出警告或者报错 int Add(int left, ...

    码小余学习C++第8天笔记

    子曰:“温故而知新,可以为师矣。”

    多态存在的意义:

    函数都能通过同一个接口调用到适合各自对象的实现方法。

    多态包括:

    • 编译时多态:函数重载就是一个例子。静态多态是编译器在编译期间完成的,编译器会根据实参类型来选择调用合适的函数,如果有合适的函数可以调用就调,没有的话就会发出警告或者报错
    int Add(int left, int right)
    {
        return left + right;
    }
    double Add(double left, int right)
    {
        return left + right;
    }
    
    int main()
    {
        Add(10, 20);      //调用的是两个int类型的那个方法
        Add(10.0,20);     //调用的是1个int类型、1个double类型的那个方法
        return 0;
    }
    
    • 运行时多态:它是在程序运行时根据基类的引用(指针)指向的对象来确定自己具体该调用哪一个类的虚函数。

    抽象类

    #include <iostream>
    using namespace std;
    
    class TakeBus {
    public:
    	void TakeBusToSubway() {
    		cout << "go to Subway--->please take bus of 318" << endl;
    	}
    	void TakeBusToStrtion() {
    		cout << "go to Station--->please Take Bus of 306 or 915" << endl;
    	}
    };
    
    //定义抽象类
    class Bus {
    public:
    	virtual void TakeBusToSomewhere(TakeBus& tb) = 0;  //纯 虚 函 数
    };
    
    //抽象类必须被继承,并且实现抽象类
    class Subway :public Bus {
    	virtual void TakeBusToSomewhere(TakeBus& tb) {
    		tb.TakeBusToSubway();
    	}
    };
    class Station :public Bus {
    	virtual void TakeBusToSomewhere(TakeBus& tb) {
    		tb.TakeBusToStrtion();
    	}
    };
    
    int main() {
    	TakeBus tb;
    	Bus* b = NULL;
    	for (int i = 1; i <= 10;i++) {
    		if ((rand() % i) & 1)
    			b = new Subway;
    		else
    			b = new Station;
    	}
    	b->TakeBusToSomewhere(tb);
    	delete b;
    	return 0;
    }
    

    分别使用对象、指针、引用来调用方法,我们发现他们输出的结果都一样。

    #include <iostream>
    using namespace std;
    
    class Base {
    public:
    	virtual void Funtest1(int i) {
    		cout << "Base::Funtest1()" << endl;
    	}
    	void Funtest2(int i) {                   //这个函数没有写virtual
    		cout << "Base::Funtest2()" << endl;
    	}
    };
    class Drived :public Base {
    	virtual void Funtest1(int i) {
    		cout << "Drived:Fubtest1()" << endl;
    	}
    	virtual void Funtest2(int i) {
    		cout << "Drived::Fubtest2()" << endl;
    	}
    };
    void TestVirtual(Base& b) {
    	b.Funtest1(1);
    	b.Funtest2(2);
    }
    int main() {
    	cout<< "----------使用对象来调用方法----------" << endl;
    	Base b;
    	Drived d;
    	TestVirtual(b);    //输出:Base::Funtest1()     Base::Funtest2()
    	TestVirtual(d);    //输出:Drived:Fubtest1()   Base::Funtest2()
    							  /*
    							  说明了(运行时多态的条件):
    								- 基类中必须包含虚函数,并且派生类中移动要对基类中的函数进行重写
    								- 通过基类对象的指针或者引用调用虚函数
    							  */
    	cout << "----------使用指针来调用方法----------" << endl;
    	Base x;
    	Drived y;
    	Base* px = &x;
    	Drived* py = &y;
    	TestVirtual(*px);
    	TestVirtual(*py);
    
    	cout << "----------使用引用来调用方法----------" << endl;
    	Base& rx = x;
    	Drived& ry = y;
    	TestVirtual(rx);
    	TestVirtual(ry);
    
    	return 0;
    }
    /*
    注意:只能通过指针或引用实现多态,而不可以使用对象
    */
    

    好好看我的代码中的注释

    展开全文
  • 多态分类

    2016-04-17 10:00:09
    1.参数多态包括函数模板和类模板2.包含多态 virtualclass A{ virtual void foo() { printf("A virtual void foo()"); }};class B : public A { void foo() { printf("B void foo()

    1.参数多态

    包括函数模板和类模板

    2.包含多态 virtual

    class A{
    
        virtual void foo() {        
            printf("A virtual void foo()");        
        }
    
    };
    
    class B : public A {
    
        void foo() {        
            printf("B void foo()");        
        }
    
    };
    
    void test() {
    
        A *a = new B();
    
        a->foo(); // B void foo()
    
    }

    3.重载多态

    重载多态是指函数名相同,但函数的参数个数或者类型不同的函数构成多态

    void foo(int);
    void foo(int, int);

    4.强制多态

    强制类型转换

    展开全文
  • 动态多态

    2017-03-04 12:08:21
    不同实现:具体实现的内容不同动态多态包括: 动态类型识别 动态绑定 动态加载 动态类型识别@interface A:NSObject -(void)draw; @end @interface B:A -(void)draw; @end @interface C:B -(void)draw; @end@...

    多态:相同接口,不同实现
    相同接口:方法的签名、参数、返回值相同
    不同实现:具体实现的内容不同

    动态多态包括:

    1. 动态类型识别
    2. 动态绑定
    3. 动态加载

    动态类型识别

    @interface A:NSObject
    -(void)draw;
    @end
    @interface B:A
    -(void)draw;
    @end
    @interface C:B
    -(void)draw;
    @end
    
    @interface D:NSObject
    -(void)paint:(A*)aA;
    //此处可改为
    //-(void)paint:(id)aA
    @end
    
    @implementation D
    -(void)paint:(A*)aA
    //此处可改为
    //-(void)paint:(id)aA
    {[aA draw];}
    @end
    
    @autoreleasepool{
    D *d = [[D alloc] init];
    A *a = [[D alloc] init];
    B *b = [[D alloc] init];
    C *c = [[D alloc] init];
    
    [d paint:a];
    [d paint:b];
    [d paint:c];
    //因为b,c对象都继承于A类,故在此可以用父类指针调用子类对象

    id类型和多态的识别原理

    • 而这里如果改成id指针类型,其不进行类型检查,可以指向属于任何类的对象,因为id类型不能说明对象的类型,而是要求对象本身提供这些信息
    • isa实例变量永远是对象的第一个实例对象, 而id指针相当于class *isa 指针,其中包含了可以储存对象各种信息的地址,在信息的传递过程中充当一个“桥梁”的作用。
    • id指针的信息传递过程就是从子类开始,一个一个往上一级寻找是否有匹配的信息,把对象和类对象匹配起来,从而实现消息的传递过程。
      isa的消息传递

    类对象的方法链表

    struct objc_method{
     SEL method_name;//方法ID
     char* method_types;//方法类型(返回值、参数)
     IMP method_imp;//方法地址(IMP)函数调用

    SEL IMP类型的作用和特点

    • SEL类型:selector,方法签名相同则ID相同,与类无关
    • IMP类型:implementation, 是一个指针类型,寻找到函数过后的函数调用,返回一个id
      typedef id(*IMP)(id, SEL,...)
    @autoreleasepool{
    Person *a = [[Person alloc] init];
    [a print];
    
    SEL act = @selector("print");//act得到print的SEL
    //或者 SEL act = NSSelectorFromString(@"print");从字符串获得方法的SEL
    //或者NSSring * name = NSStringFromSelector(act);从
    const char * sn = sel_getName(act);
    //用SEL做参数,找到指定的方法,并取得其首地址
    a.name = @"tom";
    NSLog(@"%s",sn);
    
    IMP p = [a methodForSelector:act];
    p(a, act);//p就相当于*IMP【typedef id(*IMP)】(id, SEL,...),a相当于self,act即为SEL
    for (int i=0; i<10000; i++){
                //[a print];
                p(a, act);
                }//在循环次数很多的情况下,如果用第一种方法,消息传递的方法名寻找过程很耗费时间,所以如果改进为第二种方法,可以节省很多时间
    
    }

    动态类型识别的常用方法

    如何得到类对象

    Class rectClass=[Rectangle class];
    //通过类名得到类对象
    Class aClass=[anObject class];
    //通过实例得到类对象
    if([obj1 class] == [obj2 class])
    //两个类对象是否为同类

    类(对象)和字符串

    Class someClass = NSClassFromString(@"NSView");
    id object = [[someClass alloc]init];
    //从字符串得到类对象
    NSString *view = NSStringFromClass([NSView class]);
    NSString *className = NSStringFromClass([anObject class]}
    //从类或者类对象得到类名的字符串

    判定与应用

    判定类与成员

    class-object类对象的写法:[类名或者对象名 class]

    -(BOOL)isKindOf:class-object
    //对象是不是class-object或者其子类的成员
    -(BOOL)isMemberOfClass:class-object
    //对像是不是class-object的成员
    if([object isMemberOf:[someClass class]])
    //判定是否为某个类的实例对象
    +(BOOL)isSubclassOfClass:class-object
    //对象是指定类的子类吗

    是否响应

    等号右边selector的写法:@selector(方法名、@“字符串”)

    -(BOOL)respondToSelector:selector
    //对象是否能响应selector所指定的方法
    +(BOOL)instancesRespondToSelector:selector
    //指定的类对象是否能响应selector

    应用

    -(id)performSelector:selector
    //应用selector指定的方法
    -(id)performSelector:selector withObject:object
    //应用selector指定的方法,传递参数object1
    -(id)performSelector:selector withObject:object withObject:object2
    //应用selector指定的方法,传递参数object1,传递参数object2
    ..........

    静态/动态类型识别的区别

    • 静态多态编译之前就提供了对象信息,编译时可以检查(对象和类是否匹配,对象是否拥有指定的方法等)
    • 动态多态是在编译时才确定对象信息,动态多态要求方法签名、返回值、参数类型完全一致
    • 如果不涉及到多态,尽量使用静态类型

    动态绑定

    消息派发:oc中消息一直到运行时才能绑定到对应的方法

    [receiver message];转换成函数
    objc_msgSend(receiver, selector);
    其中receivier赋值给self, selector就是方法选择器

    如果消息是
    [super message];
    这里写图片描述
    为了加快消息处理,运行时会缓存映射表,在每个类中查找方法会先在缓存中找,如果找不到再到方法映射表中去找

    动态加载

    oc允许在运行时加载一个新类,或给已有的类加载新的方法
    只需要四步
    1. #import 《objc/runtime.h>
    2. 为class pair分配空间
    3. 增加方法(class_addMethod)或者实例变量(class_addlvar)
    4. 注册新类

    #import<objc/runtime.h>//注意引入
    void sayHello(id self,SEL_cnd,NSString* aHello){
                  NSLog(@"%@",aHello);
    }
    //-(void)sayHello:(NSString*)aHello;上面函数就时这个方法的等价写法
    //void(*)(id self, SEL_cnd,NSString*);可以看出每个方法都有self和SEL两个隐含参数
    
    @autoreleasepool{
        Class parentClass=[NSObject class];
        Class newClass=objc_allocateClassPair(parentClass,"ASNewClass",0);//为class pair分配空间
        Class_addMethod(newClass,@selector(sayHello:),(IMP)sayHello,"v@:@");
        //增加方法(class_addMethod)或者实例变量(class_addlvar)
        objc_registerClassPair(newClass);//注册新类
    
        id p = [[newClass alloc] init];
        if([p respondsToSelector:@selector(sayHello:)]){
             [p performSelector:@selector(sayHello:) withObject:@"hello world!"];
             }
        }
    展开全文
  • java多态

    2016-08-27 22:30:45
    多态包括方法的重载和对象的多态。有助于分组协同开发 1 方法的动态绑定:编译器会主动把子类的方法绑定到父类之中 多态:父类的引用使用子类的方法 2 方法的重写:父类的方法在子类中重新声明 满足的条件:方法...
  • C++之多态

    热门讨论 2015-03-28 15:40:46
    这种多态包括函数模板和类模板 2.包含多态 这种多态就是C++面向对象编程的灵魂 3.重载多态 重载多态是指函数名相同,但函数的参数个数或者类型不同的函数构成多态 4.强制多态 这种多态是强制类型转换,...
  • 多态

    2019-10-21 20:33:39
    多态性包括参数化多态性和包含多态性。多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同 名问题。 多态在程序设计中,为程序提供了 [可插拔性] 多态说白了就是,一种类型,多种形态...
  • 多态的理解

    2017-08-09 19:35:00
    多态包括有静态多态和动态多态,一般静态多态是在编译期,由Java编译器根据参数列表的不同,来决定绑定相应的重载方法;多态多态是在程序运行期间,由JVM根据引用的对象,来决定绑定相应的方法。 转载于:...
  • 多态 重写 重载

    2019-04-25 14:29:00
    多态(狭义上的):同一个方法对不同的对象调用行为不同的现象。 重写:同一方法在不同类中的重新实现。 重载:不同的方法。 多态包括 重写 重载 ...
  • 什么是多态?

    2020-10-24 14:13:17
    实际上多态包括动态多态(重写)和静态多态(重载)。 静态多态 静态多态也称为编译时多态,即在编译时决定调用哪个方法; 静态多态一般是指方法重载; 只要构成了方法重载,就可以认为形成了静态多态的条件; 静态多态...
  • 什么是多态

    2020-10-23 23:31:27
    接口的多种不同的实现方式即为多态。 多态性是允许你将父对象设置成为一个或更多的他的子... 实际上多态包括动态多态(重写)和静态多态(重载)。 静态多态也称为编译时多态,即在编译时决定调用哪个方法; 静态多态一.
  • 面向对象-多态

    2018-10-27 18:23:31
    实际上多态包括动态多态和静态多态。 使用多态便于灵活的拓展我们开发的程序。 方法覆盖: 在类的继承体系结构中,如果子类中出现了与父类中有同原型的方法,那么认为子类中的方法覆盖了父类中的方法(也...
  • c++中的多态

    2016-11-16 13:06:28
    多态 一、对象的类型 1、静态类型:对象声明的的类型,是在...1、静态多态包括:函数重载和泛型编程 注意:编译器在编译期间完成的,编译器器根据函数实参的类型(可能会进行隐式类型的转换),可推断出要调用那个函...
  • C++中的多态

    千次阅读 2012-11-21 13:56:13
    这种多态包括函数模板和类模板 2.包含多态 这种多态就是C++面向对象编程的灵魂 3.重载多态 重载多态是指函数名相同,但函数的参数个数或者类型不同的函数构成多态 4.强制多态 这种多态是强制类型转换,...
  • Java中多态的理解总结

    2020-01-14 12:06:40
    其中多态包括: 编译时多态 运行时多态 编译时多态: 在编译时决定编译所需要使用的目标方法。通常是父类所决定 运行时多态: 在运行时决定运行所需要使用的目标方法,通常是子类所决定,如果子类没有重写父类该方法...
  • 多态 抽象类 接口

    2017-09-18 09:52:50
     多态包括两种 方法的重载和重写 对象的向上和向下转型 一、方法的重载和重写 重载与重写的区别: 方法重载指的是,在同一个类中,允许多个方法名相同,参数列表不同(类型,个数,顺序)即可,与访问控制符、...
  • C#静多态与动多态

    2019-04-13 16:30:29
    多态的本质:一个接口 不同实现 即多个功能 类别:静多态多态 一、静多态 函数的响应是在编译时发生的 主要包括函数重载和运算符重载两种 函数重载(overload)同一作用域 方法名相同 参数列表必须不同 返回值...
  • 强制多态包括类型强制转换等。包含多态主要通过虚函数来实现。参数多态和类模板相关。 C++中的多态只能通过指向基类类型的指针和引用来调用对象的虚函数来实现因为可以用派生类对象来初始化或者给基类类型的...
  • Java多态概念

    2013-05-17 11:33:32
    java多态包括重载和覆盖: 多态性是指允许不同类的对象对同一消息作出响应。 首先说重载(overload),是发生在同一类中。与什么父类子类、继承毫无关系。 标识一个函数除了函数名外,还有函数的参数...
  • C++的多态.cpp

    2017-01-05 23:04:13
    /*多态包括模板/重载和虚函数 *其中模板和重载属于静态多态虚函数属于动态多态 */# include using namespace std;//定义模板类template class Base {public: Base():x_(0){}; Base(T n):x_(n){}; ~Base(){ ...
  • JAVA笔记之 多态

    2017-04-25 11:43:29
    多态包括方法重写(覆盖)和方法重载 方法重写(覆盖) 方法重写(覆盖):父类与子类中的同名方法   相同点: 方法名字相同 方法的参数个数、类型相同 方法的返回类型相同 不同点: 方法完成的功能不同   ...
  • 多态包括编译时多态(通过函数重载或运算符重载实现)和运行时多态(通过类的继承关系和虚函数实现)。 在基类中用关键字virtual修饰的成员函数称为虚成员函数,可以从基类继承。 如果虚函数在类声明之外定义,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,043
精华内容 1,217
关键字:

多态包括