精华内容
下载资源
问答
  • 联编就是将模块或者函数合并在一起生成可执行代码的处理过程,同时对每个模块或者函数调用分配内存地址,并且对外部访问也分配正确的内存...按照联编所进行的阶段不同,可分为两种不同的联编方法:静态联编动态联编

    首先要介绍一下C++的多态性

    多态性可以简单地概括为“一个接口,多种方法”,程序在运行时才决定调用的函数,它是面向对象编程领域的核心概念。多态(polymorphisn),字面意思多种形状。

    C++多态性是通过虚函数来实现的,虚函数允许子类重新定义成员函数,而子类重新定义父类的做法称为覆盖(override),或者称为重写。(这里我觉得要补充,重写的话可以有两种,直接重写成员函数和重写虚函数,只有重写了虚函数的才能算作是体现了C++多态性)而重载则是允许有多个同名的函数,而这些函数的参数列表不同,允许参数个数不同,参数类型不同,或者两者都不同。编译器会根据这些函数的不同列表,将同名的函数的名称做修饰,从而生成一些不同名称的预处理函数,来实现同名函数调用时的重载问题。但这并没有体现多态性。

    多态与非多态的实质区别就是函数地址是早绑定还是晚绑定。如果函数的调用,在编译器编译期间就可以确定函数的调用地址,并生产代码,是静态的,就是说地址是早绑定的。而如果函数调用的地址不能在编译器期间确定,需要在运行时才确定,这就属于晚绑定。

    那么多态的作用是什么呢,封装可以使得代码模块化,继承可以扩展已存在的代码,他们的目的都是为了代码重用。而多态的目的则是为了接口重用。也就是说,不论传递过来的究竟是那个类的对象,函数都能够通过同一个接口调用到适应各自对象的实现方法。

    最常见的用法就是声明基类的指针,利用该指针指向任意一个子类对象,调用相应的虚函数,可以根据指向的子类的不同而实现不同的方法。如果没有使用虚函数的话,即没有利用C++多态性,则利用基类指针调用相应的函数的时候,将总被限制在基类函数本身,而无法调用到子类中被重写过的函数。因为没有多态性,函数调用的地址将是一定的,而固定的地址将始终调用到同一个函数,这就无法实现一个接口,多种方法的目的了。

    C++多态的笔试题目

    #include<iostream>
    using namespace std;
    
    class A
    {
    public:
    	void foo()
    	{
    		printf("1\n");
    	}
    	virtual void fun()
    	{
    		printf("2\n");
    	}
    };
    class B : public A
    {
    public:
    	void foo()
    	{
    		printf("3\n");
    	}
    	void fun()
    	{
    		printf("4\n");
    	}
    };
    int main(void)
    {
    	A a;
    	B b;
    	A *p = &a;
    	p->foo();
    	p->fun();
    	p = &b;
    	p->foo();
    	p->fun();
    	return 0;
    }

    第一个p->foo()和p->fuu()都很好理解,本身是基类指针,指向的又是基类对象,调用的都是基类本身的函数,因此输出结果就是1、2。
        第二个输出结果就是1、4。p->foo()和p->fuu()则是基类指针指向子类对象,正式体现多态的用法,p->foo()由于指针是个基类指针,指向是一个固定偏移量的函数,因此此时指向的就只能是基类的foo()函数的代码了,因此输出的结果还是1。而p->fun()指针是基类指针,指向的fun是一个虚函数,由于每个虚函数都有一个虚函数列表,此时p调用fun()并不是直接调用函数,而是通过虚函数列表找到相应的函数的地址,因此根据指向的对象不同,函数地址也将不同,这里将找到对应的子类的fun()函数的地址,因此输出的结果也会是子类的结果4。
      笔试的题目中还有一个另类测试方法。即

           B *ptr = (B *)&a;  ptr->foo();  ptr->fun();
      问这两调用的输出结果。这是一个用子类的指针去指向一个强制转换为子类地址的基类对象。结果,这两句调用的输出结果是3,2。
      并不是很理解这种用法,从原理上来解释,由于B是子类指针,虽然被赋予了基类对象地址,但是ptr->foo()在调用的时候,由于地址偏移量固定,偏移量是子类对象的偏移量,于是即使在指向了一个基类对象的情况下,还是调用到了子类的函数,虽然可能从始到终都没有子类对象的实例化出现.
      而ptr->fun()的调用,可能还是因为C++多态性的原因,由于指向的是一个基类对象,通过虚函数列表的引用,找到了基类中fun()函数的地址,因此调用了基类的函数。由此可见多态性的强大,可以适应各种变化,不论指针是基类的还是子类的,都能找到正确的实现方法。

    补充:

    令人迷惑的隐藏规则
    本来仅仅区别重载与覆盖并不算困难,但是C++的隐藏规则使问题复杂性陡然增加。这里“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
    1.如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)
    2.如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)

    C++纯虚函数
     一、定义
      纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加“=0” 
      virtual void funtion()=0 
    二、引入原因
       1、为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。 
       2、在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。 
      为了解决上述问题,引入了纯虚函数的概念,将函数定义为纯虚函数(方法:virtual ReturnType Function()= 0;),则编译器要求在派生类中必须予以重写以实现多态性。同时含有纯虚拟函数的类称为抽象类,它不能生成对象。这样就很好地解决了上述两个问题。
    三、相似概念
       1、多态性 
      指相同对象收到不同消息或不同对象收到相同消息时产生不同的实现动作。C++支持两种多态性:编译时多态性,运行时多态性。 
      a、编译时多态性:通过重载函数实现 
      b、运行时多态性:通过虚函数实现。 

      2、虚函数 
      虚函数是在基类中被声明为virtual,并在派生类中重新定义的成员函数,可实现成员函数的动态
    覆盖(Override)
      3、抽象类 
      包含纯虚函数的类称为抽象类。由于抽象类包含了没有定义的纯虚函数,所以
    不能定义抽象类的对象


    静态联编和动态联编

    联编就是将模块或者函数合并在一起生成可执行代码的处理过程,同时对每个模块或者函数调用分配内存地址,并且对外部访问也分配正确的内存地址,它是计算机程序彼此关联的过程。按照联编所进行的阶段不同,可分为两种不同的联编方法:静态联编动态联编。


    静态联编是指在编译阶段就将函数实现和函数调用关联起来,因此静态联编也叫早绑定,在编译阶段就必须了解所有的函数或模块执行所需要检测的信息,它对函数的选择是基于指向对象的指针(或者引用)的类型,C语言中,所有的联编都是静态联编。


    动态联编是指在程序执行的时候才将函数实现和函数调用关联,因此也叫运行时绑定或者晚绑定,动态联编对函数的选择不是基于指针或者引用,而是基于对象类型,不同的对象类型将做出不同的编译结果。C++中一般情况下联编也是静态联编,但是一旦涉及到多态和虚拟函数就必须要使用动态联编了。下面将介绍一下多态。


    多态:字面的含义是具有多种形式或形态。C++多态有两种形式,动态多态和静态多态;动态多态是指一般的多态,是通过类继承和虚函数机制实现的多态;静态多态是通过模板来实现,因为这种多态实在编译时而非运行时,所以称为静态多态。

    动态多态例子:

    #include <stdio.h>
    #include <iostream>
     
    /** 
    *Shape
    */
    class CShape
    {
    	public:
    	CShape(){}
    	virtual ~CShape(){}
     
    	virtual void Draw() = 0;
    };
     
    /**
    *Point
    */
    class CPoint : public CShape
    {
    	public:
    	CPoint(){}
    	~CPoint(){}
     
    	void Draw()
    	{
    		printf("Hello! I am Point!/n");
    	}
    };
     
    /**
    *Line
    */
    class CLine : public CShape
    {
    	public:
    	CLine(){}
    	~CLine(){}
     
    	void Draw()
    	{
    		printf("Hello! I am Line!/n");
    	}
    };
     
     
    void main()
    {
    	CShape* shape = new CPoint();
    	//draw point
    	shape->Draw();//在这里shape将会调用CPoint的Draw()函数
    	delete shape;
     
    	shape = new CLine();
    	//draw Line
    	shape->Draw();//而在这里shape将会调用CLIne 的Draw()函数
    	delete shape;
     
    	return ;
    }


    由上面的例子,大家应该能理解什么是多态了:也就是一个Draw() 可以有两种实现,并且是在运行时决定的,在编译阶段不知道,也不可能知道!只有在运行的时候才能知道我们生成的shape是那种图形,当然要实现这种效果就需要动态联编了,在基类我们会把想要多态的函数声明为虚函数,而虚函数的实现原理就使用了动态联编。


    静态多态的例子:

    在上面例子的基础之上添加模板函数:

    <pre name="code" class="cpp">template <class T>
    void DrawShape(T* t)
    {
    	t->Draw();
    }

    修改main函数为如下:

    void main()
    {
    	CShape* shape = new CPoint();
    	//draw point
    	shape->Draw();
    	DrawShape<CPoint>((CPoint*)shape);
    	delete shape;
     
    	shape = new CLine();
        //draw Line
        shape->Draw();
        DrawShape<CLine>((CLine*)sh;
        delete shape;
        return ;
    }


    
    


    展开全文
  • 静态多态性动态多态性

    千次阅读 2008-08-28 10:21:00
    动态联编支持多态性称为运行时的多态性,也称动态多态性,它是通过继承和虚函数实现的。 C++允许在参数类型不同的前提下重载函数。重载的函数与具有多态性的函数(即虚函数)不同处在于:调用正确的被重载函数实体...
     多态性的实现与静态联编、动态联编有关。静态联编支持的多态性称为编译时的多态性,也称静态多态性,它是通过函数重载和运算符重载实现的。动态联编支持的多态性称为运行时的多态性,也称动态多态性,它是通过继承和虚函数实现的。 

    C++允许在参数类型不同的前提下重载函数。重载的函数与具有多态性的函数(即虚函数)不同处在于:调用正确的被重载函数实体是在编译期间就被决定了的;而对于具有多态性的函数来说,是通过运行期间的动态绑定来调用我们想调用的那个函数实体。多态性是通过重定义(或重写)这种方式达成的。请不要被重载(overloading)和重写(overriding)所迷惑。重载是发生在两个或者是更多的函数具有相同的名字的情况下。区分它们的办法是通过检测它们的参数个数或者类型来实现的。重载与CLOS中的多重分发(multiple dispatching)不同,对于参数的多重分发是在运行期间多态完成的。
    展开全文
  • 动态联编

    千次阅读 多人点赞 2015-10-08 20:38:37
    首先我们知道的是,动态联编 和 静态联编 都是多态性的一种体现。 关于面向对象的三个基本要素:封装(类型抽象), 继承 和 多态。 首先我们从概念性上面了解了 动态联编 和 静态联编 的功能:实现了多态性。...

    关于 动态联编 和 静态联编 这个概念,自己听了老师上课讲的课仍然没有明白原理。

    那么既然这样,只能自己去学习了。


    首先我们知道的是,动态联编 和 静态联编 都是多态性的一种体现。

    关于面向对象的三个基本要素:封装(类型抽象), 继承 和 多态。

    首先我们从概念性上面了解了 动态联编 和 静态联编 的功能:实现了多态性。


    然后我们从最最基本的开始讲解。


    1.什么是 联编?

    我参考了下面这个博客:

    http://bdxnote.blog.163.com/blog/static/8444235200911311348529/


    联编是指一个计算机程序自身彼此关联的过程,在这个联编过程中,需要确定程序中的操作调用(函数调用)与执行该操作(函数)的代码段之间的映射关系;按照联编所进行的阶段不同,可分为静态联编和动态联编;

    仔细读读红色字体的那部分句子。我们就能很清楚的明白什么是联编了。给大家举个最通俗易懂的例子好了:

    A类中有fun这个函数, B类中也有fun这个函数,现在我在类外的main函数里面调用fun 函数。

    那么main函数就是函数调用,调用fun函数,

    而A类中的fun函数和B类中的fun函数就是执行该操作的代码段

    所以现在联编就是实现两者的映射关系。

    1. class A  
    2. {     
    3.     void func() {cout<<"It's A"<<endl;  
    4.   
    5. };  
    6.   
    7. class B  
    8. {     
    9.     void func() {cout<<"It's B"<<endl;  
    10.   
    11. };  
    12. int main()  
    13. {  
    14.     func();  
    15. }  
    联编就是决定将main函数中的func()的函数调用映射到A中的func函数还是B中的func函数的过程。


    2.静态联编 和 动态联编 的定义

    知道了什么事联编,那么再来理解动态联编 和静态联编也就不难了

    静态联编:
    是指联编工作是在程序编译连接阶段进行的,这种联编又称为早期联编;因为这种联编是在程序开始运行之前完成的;
    在程序编译阶段进行的这种联编又称静态束定;在编译时就解决了程序中的操作调用与执行该操作代码间的关系,确定这种关系又被称为束定;编译时束定又称为静态束定;

    拿上面的例子来说,静态联编就是在编译的时候就决定了main函数中调用的是A中的func还是B中的func。一旦编译完成,那么他们的映射关系就唯一确定了。


    动态联编:
    编译程序在编译阶段并不能确切地知道将要调用的函数,只有在程序执行时才能确定将要调用的函数,为此要确切地知道将要调用的函数,要求联编工作在程序运行时进行,这种在程序运行时进行的联编工作被称为动态联编,或动态束定,又叫晚期联编;C++规定:动态联编是在虚函数的支持下实现的;

    动态联编在编译的时候还是不知道到底应该选择哪个func函数,只有在真正执行的时候,它才确定。


    静态联编和动态联编都是属于多态性的,它们是在不同的阶段进对不同的实现进行不同的选择;

    其实多态性的本质就是选择。因为存在着很多选择,所以就有了多态。


    3.静态联编

    首先还是拿个例子来说事吧。

    1. #include <iostream>  
    2. using namespace std;  
    3. class shape{  
    4.   public:  
    5.     void draw(){cout<<"I am shape"<<endl;}  
    6.     void fun(){draw();}  
    7. };  
    8. class circle:public shape{  
    9.   public:  
    10.     void draw(){cout<<"I am circle"<<endl;}  
    11. };  
    12. void main(){  
    13.     circle  oneshape;  
    14.     oneshape.fun();  
    15. }  
    现在我们详细具体定义了一开始的A类和B类以及func函数。让我们来分析一下:

    调用oneshape.fun()的时候,进入类shape中的fun函数。

    现在我们的问题就是:fun函数调用的draw到底是shape里面的draw还是circle中的draw??

    答案是:它调用了cshape这个基类的draw函数。所以输出了 I am shape

    那么一直困扰我的问题是:为什么调用基类的draw而不是派生类中得draw呢?

    书上好像没有具体讲,上课的时候老师那也根本不会讲。

    自己想了一下,应该可以从汇编的角度理解:

    1.调用oneshape.fun()这里是一个跳转指令,进入类shape中的fun函数所在的代码段

    2.类shape的代码段是依次顺序放置的。进入fun函数后,现在我们要调用draw的地址。

    由于没有另外的数据结构来保存draw的地址,所以程序所知道的,必然只有在shape类中的draw地址了,仅仅用一个跳转指令

    在我的vs2010的反汇编调试窗口下是这样的一句代码:

    013B1546  call        shape::draw (13B10F5h) 

    很明确这里指出了shape::draw,也就确定了映射关系,完成了联编。


    4.动态联编

    从第3节中我们看到了静态联编的不足(大概教材中就是这样说的)。

    刚才我讲了,

    由于没有另外的数据结构来保存draw的地址,所以程序所知道的,必然之后在shape类中的draw地址了,仅仅用一个跳转指令

    所以我们想实现动态编联,其实就是想弄出个数据结构来,这个数据结构用来存放 映射关系,也就是联编。

    所以c++才搞了个虚函数。其实虚函数的本质就是搞了个数据结构。也就是虚函数表

    关于虚函数表,自己不想多扯了,扯出来又是一大串,大家自己去下面的博客学习吧。

    http://blog.csdn.net/haoel/article/details/1948051/

    4.1虚函数

    大家都不喜欢理解概念。我也不喜欢。但是概念毕竟是要紧的。我已经跟大家说明了为什么要引入虚函数。所以接下来请大家耐心看看下面这段概念吧:

    虚函数是动态联编的基础;虚函数是成员函数,而且是非静态的成员函数;虚函数在派生类中可能有不同的实现,当使用这个成员函数操作指针或引用所标识的对象时,对该成员函数的调用采用动态联编方式,即:在程序运行时进行关联或束定调用关系;
    动态联编只能通过指针或引用标识对象来操作虚函数;如果采用一般的标识对象来操作虚函数,将采用静态联编的方式调用虚函数;
    如果一个类具有虚函数,那么编译器就会为这个类的对象定义一个指针成员,并让这个指针成员指向一个表格,这个表格里面存放的是类的虚函数的入口地址;比如:一个基类里面有一些虚函数,那么这个基类就拥有这样一个表,它里面存放了自己的虚函数的入口地址,其派生类继承了这个虚函数表,如果在派生类中重写/覆盖/修改了基类中的虚函数,那么编译器就会把虚函数表中的函数入口地址修改成派生类中的对应虚函数的入口地址;这就为类的多态性的实现提供了基础;

    虚函数按照其声明顺序存放于虚函数表中;
    父类的虚函数存放在子类虚函数的前面;
    多继承中,每个父类都有自己的虚函数表;
    子类的成员函数被存放于第一个父类的虚函数表中;

    4.2动态联编举例

    最后给大家举个例子,在第三节的那个例子上,下面的代码仅仅是多了一个词:virtual

    1. #include <iostream>  
    2. using namespace std;  
    3. class shape{  
    4.   public:  
    5.     void virtual draw(){cout<<"I am shape"<<endl;}//这里设定了draw是虚函数  
    6.     void fun(){draw();}  
    7. };  
    8. class circle:public shape{  
    9.   public:  
    10.     void draw(){cout<<"I am circle"<<endl;}//虽然没有说明circle类中的draw是虚函数,但是circle其实继承了virtual性质  
    11. };  
    12. void main(){  
    13.     circle  oneshape;  
    14.     oneshape.fun();  
    15. }  
    现在我们再来运行一下程序,输出就变成了I am circle

    这里的反汇编代码贴出来,自己有空看看:

    1. 00CC15A0  push        ebp    
    2. 00CC15A1  mov         ebp,esp    
    3. 00CC15A3  sub         esp,0CCh    
    4. 00CC15A9  push        ebx    
    5. 00CC15AA  push        esi    
    6. 00CC15AB  push        edi    
    7. 00CC15AC  push        ecx    
    8. 00CC15AD  lea         edi,[ebp-0CCh]    
    9. 00CC15B3  mov         ecx,33h    
    10. 00CC15B8  mov         eax,0CCCCCCCCh    
    11. 00CC15BD  rep stos    dword ptr es:[edi]    
    12. 00CC15BF  pop         ecx    
    13. 00CC15C0  mov         dword ptr [ebp-8],ecx    
    14. 00CC15C3  mov         eax,dword ptr [this]    
    15. 00CC15C6  mov         edx,dword ptr [eax]    
    16. 00CC15C8  mov         esi,esp    
    17. 00CC15CA  mov         ecx,dword ptr [this]    
    18. 00CC15CD  mov         eax,dword ptr [edx]    
    19. 00CC15CF  call        eax    
    20. 00CC15D1  cmp         esi,esp    
    21. 00CC15D3  call        @ILT+440(__RTC_CheckEsp) (0CC11BDh)    
    22. 00CC15D8  pop         edi    
    23. 00CC15D9  pop         esi    
    24. 00CC15DA  pop         ebx    
    25. 00CC15DB  add         esp,0CCh    
    26. 00CC15E1  cmp         ebp,esp    
    27. 00CC15E3  call        @ILT+440(__RTC_CheckEsp) (0CC11BDh)    
    28. 00CC15E8  mov         esp,ebp    
    29. 00CC15EA  pop         ebp    
    30. 00CC15EB  ret    
    展开全文
  • 这个主要是虚函数实现的多态性. 1.静态联编 静态联编是指联编工作在编译阶段完成的,这种联编过程是在程序运行之前完成的,又称为早期联编。要实现静态联编,在编译阶段就必须确定程序中的操作调用(如函数...
    静态联编说的是在编译时就已经确定好了调用和被调用两者的关系
    动态联编说的是程序在运行时才确定调用和被调用者的关系.
    这个主要是虚函数实现的多态性.

    1.静态联编

    静态联编是指联编工作在编译阶段完成的,这种联编过程是在程序运行之前完成的,又称为早期联编。要实现静态联编,在编译阶段就必须确定程序中的操作调用(如函数调用)与执行该操作代码间的关系,确定这种关系称为束定,在编译时的束定称为静态束定。静态联编对函数的选择是基于指向对象的指针或者引用的类型。其优点是效率高,但灵活性差。


    1:静态联编

    #include"iostream.h"
    class A
    {
    public:
    	void fun()
    	{
    		cout << "A" << "";
    	}
    };
    
    class B : public A
    {
    public:
    	void fun()
    	{
    		cout << "B" << endl;
    	}
    };
    
    void main()
    {
    	A* pa = NULL;
    	A a;
    	B b;
    	pa = &a; pa->fun();
    	pa = &b; pa->fun();
    }

    该程序的运行结果为:A   A


    从例1程序的运行结果可以看出,通过对象指针进行的普通成员函数的调用,仅仅与指针的类型有关,而与此刻指针正指向什么对象无关要想实现当指针指向不同对象时执行不同的操作,就必须将基类中相应的成员函数定义为虚函数,进行动态联编。


    2.动态联编

    动态联编是指联编在程序运行时动态地进行,根据当时的情况来确定调用哪个同名函数,实际上是在运行时虚函数的实现。这种联编又称为晚期联编,或动态束定。动态联编对成员函数的选择是基于对象的类型,针对不同的对象类型将做出不同的编译结果。C++中一般情况下的联编是静态联编,但是当涉及到多态性和虚函数时应该使用动态联编。动态联编的优点是灵活性强,但效率低。


    动态联编规定:只能通过指向基类的指针或基类对象的引用来调用虚函数,

    其格式为:指向基类的指针变量名->虚函数名(实参表) 或  基类对象的引用名.虚函数名(实参表)


    实现动态联编需要同时满足以下三个条件:

     必须把动态联编的行为定义为类的虚函数。

     类之间应满足子类型关系,通常表现为一个类从另一个类公有派生而来。

    ③ 必须先使用基类指针指向子类型的对象,然后直接或者间接使用基类指针调用虚函数。


    2:动态联编

    #include "iostream.h"
    class A
    {
    public:
    	Virtual void fun()   //虚函数
    	{
    		cout << "A" << "";
    	}
    };
    
    class B : public A
    {
    public:
    	Virtual void fun()    //虚函数
    	{
    		cout << "B" << endl;
    	}
    };
    
    void main()
    {
    	A* pa = NULL;
    	A a;
    	B b;
    	pa = &a; pa->fun();
    	pa = &b; pa->fun();
    }

    该程序的运行结果为:A  B


    从例2程序的运行结果可以看出,将基类A中的函数fun定义为虚函数后,当指针指向不同对象时执行了不同的操作,实现了动态联编。


    3.动态联编分析

       动态联编要求派生类中的虚函数与基类中对应的虚函数具有相同的名称、相同的参数个数和相同的对应参数类型、返回值或者相同,或者都返回指针或引用,并且派生类虚函数所返回的指针或引用的基类型是基类中虚函数所返回的指针或引用的基类型的子类型。如果不满足这些条件,派生类中的虚函数将丢失其虚特性,在调用时进行静态联编。


    3:通过指向基类的指针来调用虚函数

    #include"iostream.h"
    Class base
    {
    public:
    	virtual void fun1() { cout << "base fun1" << endl; }
    	virtual void fun2() { cout << "base fun2" << endl; }
    	void fun3() { cout << "base fun3" << endl; }
    	void fun4() { cout << "base fun4" << endl; }
    };
    Class derived : public base
    {
    public:
    	Virtual void fun1() { cout << "derived fun1" << endl; }
    	Virtual void fun2(intx) { cout << "derived fun2" << endl; }
    	Virtual void fun3() { cout << "derived fun3" << endl; }
    	Void fun4() { cout << "derived fun4" << endl; }
    };
    
    void main()
    {
    	base* pb;
    	derived d;
    	pb = &d;
    
    	//通过指向基类的指针来调用虚函数
    	pb->fun1();
    	pb->fun2();
    	pb->fun3();
    	pb->fun4();
    }

    该程序的运行结果:

    Derived fun1     base fun2      base fun3      base fun4


    本例中函数fun1在基类base和派生类derived中均使用了关键字virtual定义为虚函数,并且这两个虚函数具有相同的参数个数、参数类型和返回值类型。因此,当指针pb访问fun1函数时,采用的是动态联编。

    函数fun2在基类base和派生类de-rived中定义为虚函数,但这两个虚函数具有不同的参数个数。函数fun2丢失了其虚特性,在调用时进行静态联编。

    函数fun3在基类base中说明为一般函数,在派生类derived中定义为虚函数。在这种情况下,应该以基类中说明的成员函数的特性为标准,即函数fun3是一般成员函数,在调用时采用静态联编。

    函数fun4在基类base和派生类derived中均说明为一般函数,因此基类指针pb只能访问base中的成员。


    4:通过基类对象的引用来调用虚函数

    #include"iostream.h"
    Class CPoint
    {
    public:
    	CPoint(doublei,doublej) { x = i; y = j; }
    	Virtual double Area() { return 0.0; }
    private:
    	doublex,y;
    };
    
    Class CRectangle : public CPoint
    { public:
    	CRectangle(double i,double j,double k,double l);
    	Double Area() { return w*h; }
    private:
    	double w,h;
    };
    
    CRectangle::CRectangle(double i,double j,double k,double l) :CPoint(i, j)
    {
    	w = k;
    	h = l;
    }
    
    Void fun(CPoint &s)
    {
    	//通过基类对象的引用来调用虚函数
    	cout << s.Area() << endl;
    }
    
    void main()
    {
    	CRectangle rec(3.0, 5.2, 15.0, 25.0);
    	fun(rec);
    }

    该程序的运行结果为:375


    4中的成员函数Area在基类CPoint中使用了关键字virtual定义为虚函数,在派生类CRectangle中定义为一般函数,但是进行了动态联编,结果为15*25375。这是因为一个虚函数无论被公有继承多少次,它仍然保持其虚特性。在派生类中重新定义虚函数时,关键字virtual可以写也可不写,但为了保持良好的编程风格,避免引起混乱时,应写上该关键字


    4.小结

       从以上四个例子中可以看出:虚函数是实现多态的基础,是实现动态联编的必要条件之一动态联编要靠虚函数来实现,虚函数要靠动态联编的支持。两者相辅相成,缺一不可。


    展开全文
  • 多态性

    2015-01-08 10:18:21
    1.多态性是指用一个名字定义... 动态联编支持多态性称为运行时的多态性,也成动态多态性,它是通过继承和虚函数实现的。 3.函数重载的意义在于它能用同一个名字访问一组相关的函数  在类中普通成员函数和构造函数
  • 静态联编动态联编

    2019-03-16 18:25:03
    首先我们知道的是,动态联编 和 静态联编 都是多态性的一种体现。 关于面向对象的三个基本要素:封装(类型抽象), 继承 和 多态。 首先我们从概念性上面了解了 动态联编 和 静态联编 的功能:实现了多态性。 ...
  • 5.1 编译时的多态性与运行时的多态性 1、什么时多态性 【定义】 (1)在面向对象方法中,不同对象收到相同的消息时,产生不同的行为(即方法) (2)在C++程序设计中,多态性是指用一个名字定义不同的函数,函数...
  • 首先我们知道的是,动态联编 和 静态联编 都是多态性的一种体现。 关于面向对象的三个基本要素:封装(类型抽象), 继承 和 多态。 首先我们从概念性上面了解了 动态联编 和 静态联编 的功能:实现了多态性。...
  • C++多态性原理详解(静态多态、动态...静态联编支持多态性称为编译时多态(静态多态),通过函数重载或函数模板实现;动态联编支持多态性称为运行时多态(动态多态),通过虚函数表实现。 2 静态多态性 2.1 函数重载 首
  • 按照联编所进行的阶段不同,可分为两种不同的联编方法:静态联编动态联编。  1. 静态联编 静态联编是指联编工作在编译阶段完成的,这种联编过程是在程序运行之前完成的,又称为早期联编。要实现静态联编,在...
  • 关于静态联编动态联编的分析

    千次阅读 2013-06-14 09:44:57
    按照联编所进行的阶段不同,可分为两种不同的联编方法:静态联编动态联编。 1. 静态联编 静态联编是指联编工作在编译阶段完成的,这种联编过程是在程序运行之前完成的,又称为早期联编。要实现静态联编,在编译...
  • 按照联编所进行的阶段不同,可分为静态联编动态联编。 例如A类中有fun这个函数, B类中也有fun这个函数,现在我在类外的main函数里面调用fun 函数。那么main函数就是函数调用,调用fun函数,而A...
  • C++的多态性

    2007-08-01 09:20:00
    C++的多态性 1.多态性的概念 多态性是指用一个名字定义不同的函数,这函数执行不同但又类似...动态联编支持多态性称为运行时的多态性,也称动态多态性,它是通过继承和虚函数实现的。 C++的多态性 2.函数重载函数重
  • 动态联编与静态联编的区别

    千次阅读 2019-03-18 14:40:57
    摘要】:本文阐述了静态联编动态联编的概念和...按照联编所进行的阶段不同,可分为两种不同的联编方法:静态联编动态联编。 1.静态联编 静态联编是指联编工作在编译阶段完成的,这种联编过程是在程序运行...
  • 按照联编所进行的阶段不同,可分为两种不同的联编方法:静态联编动态联编。 1. 静态联编        静态联编是指联编工作在编译阶段完成的,这种联编过程是在程序运行...
  • 动态联编所支持多态性称为运行时的多态性,由虚函数来支持 2.静态联编 对象的内存地址空间中只包含数据成员,并不存储有关成员函数的信息 3.动态联编 C++要实现动态联编,调用函数时必须使用派生类指针 使用...
  • 习题五 多态性和虚函数

    千次阅读 2018-06-27 21:54:00
    (2)C++支持两种多态性,静态联编所支持多态性称为 编译时的多态性 、动态联编所支持多态性称为 运行时的多态性 。(3)重载函数在编译时表现出多态性,就是 静态 联编;而虚函数则在运行时表现出多态...
  • 摘要】:本文阐述了静态联编动态联编的概念和区别,通过具体...按照联编所进行的阶段不同,可分为两种不同的联编方法:静态联编动态联编。  1. 静态联编 静态联编是指联编工作在编译阶段完成的,这种
  • C++的多态性

    千次阅读 2005-06-25 22:47:00
    1.多态性的概念 多态性是指用一个名字定义不同的...动态联编支持多态性称为运行时的多态性,也称动态多态性,它是通过继承和虚函数实现的。2.函数重载函数重载的意义在于他能用同一个名字访问一组相关的函数。在类

空空如也

空空如也

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

动态联编所支持的多态性称为