C++多态性,虚函数,重载,抽象类

来源:互联网 发布:html软件 编辑:程序博客网 时间:2024/06/04 18:28

1,C++多态性,虚函数,重载,纯虚函数,比较有意思,,,,,,


在面向对象的程序中,当不同的对象接受到相同的消息产生不同的动作,这种性质称为多态性。

简单的来说:就是指用一个名字定义不同的函数,这些函数执行不同但有类似的操作,即所谓的同样的接口访问功能不同的函数,从而实现“一个接口,多种方法”


例如:比较两个数的大小,我们可以针对不同的数据类型(整型,浮点型,双精度型),写出多个不同名称的函数来

实现。但是,事实上,由于它们的功能完全相同,所以可以利用多态性来完成这些功能。。。。

int Max(int i, int j){        return i>j?i:j; }float Max(float i, float j){        return i>j?i:j; }
           鉴于此:如果3,4或着1.2,3.4比较大小的时候,那么Max(3,4)和Max(1.2,3.4)被调用时,编译器会自

                           判断调用哪一个函数。。。。。。


多态性与联编(编译)


          在面向对象的语言中,多态性的实现和联编密不可分。联编(binding,绑定或装配)是将一个标识符名和一个

          存储地址联系在一起。。。。一个源程序经过编译,连接,最后生成可执行代码,就是将部分的执行代码联编

          在一起的过程。。。。


          一般而言,联编方式有两种:静态联编(static binding)和动态联编(dynamic binding)。静态联编是联编在一

          个源程序经过编译,连接,成为可执行文件这个过程中的编译阶段完成的,即联编过程是在运行之前完成的,,

          动态联编:是指联编在一个源程序经过编译,连接,成为可执行文件的这个过程中的程序执行阶段完成的,即

          联编过程是在程序运行时才动态完成的,,,,


          C++实现多态性有以下两种情况

          1,编译时多态行:通过函数重载,运算符重载,模板重载

          2,运行时多态:借助虚函数来实现


虚函数声明:

virtual   函数类型    函数名称   (形式参数表);
           虚函数的声明只能出现在类声明的函数原型声明中,,,在派生类中可以不显示的声明为虚函数,当调用此成员

           函数时,C++系统会自动判断调用哪一个对象的成员函数。。。。


           虚函数:单界面多实现

           基类中用虚函数定义了一个函数的原型(提供一个界面),那么就提供了公有继承对象的一个公共的界面,而

           多个派生类重新定义虚函数的函数体内容(多个实现版本),将基类的指针指向派生类对象,从而达到虚函数

           不同实现的目的。。。。。。。。


代码1:

#include <iostream>using namespace std;class shape{        public:                float area()                {     return -1;}       };class circle:public shape{        float radius;        public:                circle(float r)                {     radius = r;}                float area()                {     return 3.14*radius*radius;}       };int main(){        shape obj, *ptr;        circle c(3.4);                ptr = &obj;        cout<<ptr->area()<<endl;                ptr = &c;        cout<<ptr->area()<<endl;        }

程序运行结果:

结果为什么会是这样的呢???我们希望的是求圆的面积啊,,,,,

这是因为在这里只是用到了静态编译,,,,所以前后两次ptr->area()都调用了基类中的area函数,,,,

但是,,,值得注意的是:对于我们的变量,如果有相同的话,那么就会调用派生类中的值。。。。。。。。。


而如果,我们把代码改成了这样:


#include <iostream>using namespace std;class shape{        public:                virtual  float area()//跟上面的程序相比,只有这里不一样                {     return -1;}       //关键字virtual说明此函数为虚函数};class circle:public shape{        float radius;        public:                circle(float r)                {     radius = r;}                float area()      //在派生类中,虚函数被重新定义以实现不同的操作。                {     return 3.14*radius*radius;}       //这种方式称为:函数超越,或者函数覆盖。。。。};int main(){        shape obj, *ptr;        circle c(3.4);                ptr = &obj;        cout<<ptr->area()<<endl;                ptr = &c;        cout<<ptr->area()<<endl;        }

运行结果:


概述:

1,virtual关键字:指示C++编译器对该函数的调用进行动态联编

2,尽管可以用对象名和点运算符的方式调用虚函数,即向对象发送消息tri.area()或者rect.area()。这同样是静态联编

3,只有当访问虚函数是通过基类指针s(或者引用)时才可获得运行时的多态行。。。。

4,在派生类中,虚函数被重新定义时,其函数的原型必须与基类中的函数原型完全相同,,,,,,,,,,,,,

      也就是说:什么都一样,就是函数体的实现完全不同

5,必须在继承的体系下实现。。。。。,必须是类的成员函数,普通函数不可能是虚函数。。。。。。

6,虚函数不能是静态成员函数,,,,因为静态成员函数的存储位置说明了它不受限与某个具体的对象。。。。。

7,根据特性,运行时确定,所以内联函数自然而然也就不是虚函数。。。。。。

8,构造函数不能是虚函数,析构函数可以是:因为构造时,对象还是一片未定型的空间。。。。只有在构造完成后,

      对象才能成为一个类的名副其实的实例,,,,,、



由此,我们再次简单的总结一下:

虚函数,声明定义一定在基类中,其他的实现可能在派生类中(前提是:函数原型基本一样),这样,通过基类的指

针(引用)跟派生类的对象挂钩,那么调用的就是派生类中的函数了,,,实现了多态。。。。


这里可能跟函数重载有一定关系::

#include <iostream>using namespace std;class base{        public:                virtual void func1();                virtual void func2();                virtual void func3();                void fun4();    };class derived:public base{        public:                virtual void func1();     //virtual  ,虚函数的再次实现                void func2(int x);                char func3();             //错误,返回类型不同                void func4();             //普通函数的重载,不是虚函数,,,};void main(){        base d1,*bp;        derived d2;                bp = &d2;        bp->func1();       //调用父类func1()                                                                                                          bp->func2();       //调用基类func2()        bp->func3();       //调用基类func3()        bp->func4();       //调用基类func4()  }

虚函数:只能是成员函数,,,,,重载函数:没有要求

虚函数:不同调用通过不同的对象指向或引用

重载函数:根据参数的类型或个数来进行不同的调用。。。。。。



什么函数不能声明为虚函数?

普通函数(非成员函数)、静态成员函数、内联成员函数、构造函数、友元函数


虚函数通过继承方式来体现出多态作用,它必须是基类的非静态成员函数,其访问权限可

以是protected或public,在基类的类定义中定义虚函数的一般形式是:


virtual 函数返回值类型虚函数名(形参表){ 函数体 }


常见的不能声明为虚函数的有:普通函数(非成员函数)、静态成员函数、内联成员函数、构造函数、友元函数。

(1)普通函数不能声明为虚函数。普通函数(非成员函数)只能被重载(overload),不能被重写(override),声明为虚函数也没有什么意思,因此编译器会在编译时绑定函数。


(2) 构造函数不能声明为虚函数。构造函数一般用来初始化对象,只有在一个对象生成之后,才能发挥多态作用。如果将构造函数声明为虚函数,则表现为在对象还没有生成的时候来定义它的多态,这两点是不统一的。另外,构造函数不能被继承,因而不能声明为虚函数。


(3) 静态成员函数不能声明为虚函数。静态成员函数对于每个类来说只有一份代码,所有的对象都共享这份代码,它不归某个对象所有,所以也没有动态绑定的必要性。


(4) 内联(inline)成员函数不能声明为虚函数。内联函数就是为了在代码中直接展开,减少函数调用开销的代价。虚函数是为了在继承后对象能够准确的执行自己的动作,这是不可能统一的。另外,内联函数在编译时被展开,虚函数在运行时才能动态的绑定函数。

(5) 友元函数不能声明为虚函数。友元函数不属于类的成员函数,不能被继承。


设置虚函数时须注意以下几点:


  • 只有类的成员函数才能说明为虚函数;

  • 静态成员函数不能是虚函数;

  • 内联函数不能为虚函数;

  • 构造函数不能是虚函数;

  • 析构函数可以是虚函数,而且通常声明为虚函数。



纯虚函数:

纯虚函数是在基类中说明的虚函数,它在该基类中没有定义具体的操作内容,而在各个派生类中根据实际需要定义

自己的实现。。。。。。

virtual  函数类型  函数名称(函数参数表) = 0;
从声明格式看:纯虚函数是在虚函数成员的后面加上“= 0”,纯虚函数是根本没有函数体的,当声明为纯虚函数,那么

为一个类的名副其实的实例,,,,,、



由此,我们再次简单的总结一下:

虚函数,声明定义一定在基类中,其他的实现可能在派生类中(前提是:函数原型基本一样),这样,通过基类的指

针(引用)跟派生类的对象挂钩,那么调用的就是派生类中的函数了,,,实现了多态。。。。


这里可能跟函数重载有一定关系:

基类中就不能给出函数的实现部分,其函数体是由基类的派生类给出。。。。。。。。。。


可以这样说:

有一个动物类,,,,继承有狮子,老虎等,当没有具体的动物时,我们探究它的是不是哺乳动物(作为一个函数)

 就没有一点价值

了,所以,此刻的动物类就是一个抽象类,,,,也就是说:动物类这个基类里面没有这个函数的实现,也不能有


那么什么是抽象类(含有纯虚函数的类,抽象类)

class animal{        public:                virtual void ISLEG_NUMBER() = 0;        };int main(){        animal pig;       //错误,抽象类中不能存在对象        animal *p;        //正确,可以声明指向动物类(抽象类)的指针        animal f();       //错误,抽象类不能作为函数的返回类型        g(animal);        //错误,抽象类不能作为函数的参数类型  }

抽象类,一定不能有对象,,,,,,,,,,,,,,,,,,,,,,,,,,



0 0