zoukankan      html  css  js  c++  java
  • C++语言笔记系列之十八——虚函数(1)

    1.C++中的多态
    (1)多态性:同一个函数的调用能够进行不同的操作,函数重载是实现多态的一种手段。


    (2)联编:在编译阶段进行联接。即是在编译阶段将一个函数的调用点和函数的定义点联接起来。
    A.静态联编:在编译阶段就完毕的函数联编——函数重载。


    B.动态联编:在程序的执行阶段由系统自己主动选择详细的函数——虚函数。


    注:C++的多态主要指的就是动态联编。
    2.虚函数
    (1)虚函数是在函数的定义时将其声明为虚函数就可以。
    (2)说明:virtual 数据类型 函数名(參数表) {函数体}
    A.目的:当通过基类指针调用虚函数时,系统进行动态联编。
    B.在派生类中定义了和基类名称同样、參数个数同样、參数类型同样、返回值类型同样的函数时。若基类中的同名成员函数是虚函数,派生类的同名成员函数将自己主动被虚化。


    C.通过对象訪问虚函数时採用静态联编。

    动态联编仅仅可以通过指向对象的指针和对象的引用来完毕。
    3.样例
    example 1

    #include <iostream.h>

    class Point(
    {
    private:
        float x, y;
    public:
        void setpoint(float i, float j)
        {
            x = i; y = j;
        }
        float area() {return 0.0;}//等价于:float area()=0;
    };
    const float PI=3.1416;
    class Circle:pulic Point
    {
    private:
        float rad;
    public:
        void setrad(float r) {rad = r;}
        float area() {return PI*rad*rad;}
    };
    int main()
    {
        Point p;
        Circle c;
        float a = p.area();
        cout<<a<<endl;
        c.setrad(2.5);
        a = c.area();
        cout<<a<<endl;
    }
    分析:
    float a=p.area()调用的是Point类的area函数:p是基类Point的对象;对象调用函数时採用静态联编;Point类的对象调用area,area一定是Point类的area;a=c.area()调用的是Circle类的area函数,由于就近原则。


    改动main函数:

    int main()
    {
        Point *p;
        Circle c;
        float a;
        c.setrad(2.5);
        p = &c;
        a = p->area();//该函数属于静态联编
        cout<<a<<endl;
    }
    程序输出:
    0.0
    分析:虽然基类指针指向了派生类对象,但仅仅可以通过基类的指针引用派生类对象从基类继承过去的成员。假设要想基类指针可以指向派生类的成员,那么就要使用虚函数,从而实现动态联编。


    4.四种静态联编
    (1)基类指针指向基类对象,採用静态联编调用基类成员。
    (2)派生类的指针指向派生类的对象採用静态联编,调用派生类成员。若派生类没有的成员。调用基类的该成员。


    (3)基类指针指向派生类对象,採用静态联编,调用基类成员。


    (4)对象引用成员一定是静态联编。基类对象引用基类成员,派生类对象引用派生类成员,若派生类没有则能够引用基类成员。
    注:派生类指针不可指向基类,引用也不能够。
    5.动态联编
    (1)条件:基类的同名函数被声明为虚函数。基类指针指向派生类对象。


    (2)作用:使用动态联编。使得一个基类指针能够訪问多个派生类的成员函数,动态联编仅仅能通过指针或引用来实现(前提是虚函数机制)。
    example 2

    #include <iostream.h>

    class Point
    {
    public:
        virtual double area() {return 0.0;}
    };
    class Rectangle:public Point
    {
        double length, width;
    public:
        Rectangle(double l, double w):length(l), width(w) {}
        double area() {return length*width;}
    };
    class Circle:public Point
    {
        double radium;
    public:
        Circle(double r) {radium = r;}
        double area() {return 3.1416*radium*radium;}
    };
    class Triangle:public Point
    {
        double x, y, z;
    public:
        Triangle(double a, double b, double c)
        {x = a; y = b; z = c;}
        double area() {return (x+y+z)*0.5;}
    };
    int main()
    {
        Point *p;
        Rectangle r(5.0, 2.0);
        Circle c(6.0);
        Triangle t(3.0, 4.0, 5.0);
        p = &r;
        cout<<p->area()<<endl;
        p = &c;
        cout<<p->area()<<endl;
        p = &t;
        cout<<p->area()<<endl;
    }
    程序输出:
    10
    113.098
    6
    注:基类的虚函数也能够被调用。调用方法:加类的作用域(强制採用静态联编)。
    演示样例:
    p = &r;
    p->area();//调用Rectangle类的area函数
    p->Point::area();//强制採用静态联编,调用Point类作用下的area函数
    6.虚函数的訪问权限
    (1)派生类中的虚函数不影响动态联编,基类的虚函数是保证动态联编的必要条件。
    (2)一个类中的虚函数仅仅对派生类重定义的函数有影响,对它的基类成员无影响。


    example 3

    #include <iostream.h>

    class A
    {
    public:
        virtual void fun1()
        {cout<<"fun1()...fun2()"<<endl; fun2();}
        void fun2()
        {cout<<"fun2()...fun3()"<<endl; fun3();}
        virtual void fun3()
        {cout<<"fun3()...fun4()"<<endl; fun4();}
        virtual void fun4()
        {cout<<"fun4()...fun5()"<<endl; fun5();}
        void fun5() {cout<<"The end."<<endl;}
    };
    class B:public A
    {
        void fun3()
        {cout<<"fun3...fun4"<<endl; fun4();}
        void fun4()
        {cout<<"fun4...fun5"<<endl; fun5();}
        void fun5()
        {cout<<"All done."<<endl;}
    };
    int main()
    {
        A *thing;
        thing = new A;
        thing->fun1();
        thing = new B;
        thing->fun1();
    }
    程序输出:
    fun1()...fun2()
    fun2()...fun3()
    fun3()...fun4()
    fun4()...fun5()
    The end.
    fun1()...fun2()
    fun2()...fun3()
    fun3...fun4
    fun4...fun5
    All done.
    7.若基类和派生类中的同名函数參数个数不同。參数类型不同,基类中的成员函数虽然是虚函数。但将丢失虚特性——採用静态联编。
    基类和派生类中的同名成员函数,若出现基类中的同名成员函数非虚,派生类中同名成员函数是虚函数。那么也将採用静态联编。
    example 4

    #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 Device:public Base
    {
    public:
        virtual void fun1()
        {cout<<"Device fun1."<<endl;}
        virtual void fun2()
        {cout<<"Device fun2."<<endl;}
        virtual void fun3()
        {cout<<"Device fun3."<<endl;}
        virtual void fun4()
        {cout<<"Device fun4."<<endl;}
    };
    int main()
    {
        Base *pb;
        Device d;
        pb = &d;
        pb->fun1();
        pb->fun2();
        pb->fun3();
        pb->fun4();
    }
    程序输出:
    Device fun1.
    Device fun2.
    Base fun3.
    Base fun4.

  • 相关阅读:
    JavaScript条件判断和循环
    JavaScript数据类型详解
    Dockerfile使用
    让ie8、ie9支持媒体查询
    事件穿透
    判断是苹果手机还是安卓手机
    ES6中字符串的扩展
    ES6数组的扩展运算符
    let和const
    ES6中函数的扩展
  • 原文地址:https://www.cnblogs.com/liguangsunls/p/7156698.html
Copyright © 2011-2022 走看看