zoukankan      html  css  js  c++  java
  • C++虚函数

      我们都知道基类的指针和引用可以在不进行显示转换指向派生类,如下,反之不可。

    clas base{};
    class derive:public base{};
    derive d;
    base &pt = &d;
    base &pr = &d;
    

      那么它会调用哪个函数?

    非虚函数版

    #include <iostream>
    using namespace std;
    class base
    {
    public:
        void f(){cout << "base" << endl;}
    };
    class derive:public base
    {};
    int main()
    {
        base b;
        derive d;
        b.f();//base
        d.f();//base,继承void base::f()
        base &pr = d;
        pr.f();//base
        return 0;
    }
    

      如果派生类也有个相同的函数,那么基类的同名函数会被覆盖。

    #include <iostream>
    using namespace std;
    class base
    {
    public:
        void f(){cout << "base" << endl;}
    };
    class derive:public base
    {
    public:
       void f(){cout << "derive" << endl;}//覆盖掉void base::f()
    };
    int main()
    {
        base b;
        derive d;
        b.f();//base
        d.f();//derive
        base &pb = d;
        pb.f();//base
        derive &pd = d;
        pd.f();//derive
        return 0;
    }
    

      上面的例子可以知道函数的版本是根据指针或引用的类型来选择的,而不是指向的对象的类型。

      如果想调用base的f(),可以通过d.A::f();pd.A::f();进行调用。关于这个讨论,可以参考基类和派生类之间的同名函数,存在重载吗?

    虚函数(virtual fuction)

      如果函数为虚函数,通过指针或引用调用此函数,函数的版本是根据指向的对象类型,而不是指针或引用的类型。

    #include <iostream>
    using namespace std;
    class base
    {
    public:
        virtual void f(){cout << "base" << endl;}
    };
    class derive:public base
    {};
    int main()
    {
        base b;
        derive d;
        b.f();//base
        d.f();//base
        base &pb = b;
        pb.f();//base
        derive &pd = d;
        pd.f();//base
        return 0;
    }
    

      由于derive没有定义void f(),所以继承void base::f()

    #include <iostream>
    using namespace std;
    class base
    {
    public:
        virtual void f(){cout << "base" << endl;}
    };
    class derive:public base
    {
    public:
       void f(){cout << "derive" << endl;}
    };
    int main()
    {
        base b;
        derive d;
        b.f();//base
        d.f();//derive
        base &pb = b;
        pb.f();//base
        derive &pd = d;
        pd.f();//derive
        return 0;
    }
    

      虽然在基类的函数加上virtual后,派生类的相应函数就自动为虚函数,但是我们一般还是在派生类相应的函数加上virtual。一方面是为了让自己知道这是一个虚函数,另一方面是为了一脉继承,也就是说让derive的派生类的相应函数也为虚函数。

    重新定义

      上面的说法,通俗地讲就是‘子类重载父类的方法’,这种作法是不存在。

    #include <iostream>
    using namespace std;
    class base
    {
    public:
        virtual void f(){cout << "base" << endl;}
    };
    class derive:public base
    {
    public:
       virtual void f(int n){cout << "derive" << endl;}//将隐藏基类所有名为f的函数,即void base::f()
    };
    int main()
    {
        derive d;
        d.f();//提示参数过少
        return 0;
    }
    

      这告诉我们:①如果派生类重新定义虚函数,应确保与原来的原型一样,但如果返回值是基类的指针或者引用,则可以修改为派生类的指针或者引用,这种特性叫做返回类型协变(covariance of return type)。②如果基类的函数被重载,应该在派生类定义所有基类版本,避免基类函数被覆盖。

    class base
    {
    public:
    	virtual void f();
    	virtual void f(int);
    };
    class derive
    {
    public:
        virtual void f();
        virtual void f(int);
    };
    

      在第一份代码中,如果想使用d.f(),只需如下修改。

    class derive:public base
    {
    public:
    	using base::f;
       virtual void f(int n){cout << "derive" << endl;}//将隐藏基类所有名为f的函数,即void base::f()
    };
    

    易错点

    #include <iostream>
    using namespace std;
    class base
    {
    public:
        base()
        {
            cout << "creat" << endl;
        }
        virtual void f(){cout << "base" << endl;}
    };
    class derive:public base
    {
    public:
       void f(){cout << "derive" << endl;}
    };
    void fv(base t)
    {
        t.f();
    }
    int main()
    {
        derive d;
        fv(d);//base
        return 0;
    }
    

      上述fv的参数是传值,会导致讲d的base部分复制给t(也就是调用base::base()),进而调用base::f()。所以应该讲参数改为引用或者指针。

    注意事项

    1. 构造函数不能是虚函数。派生类的构造函数会自动调用基类的构造函数。
    2. 析构函数应当是虚函数。如果A是基类,B是派生类,B类对象过期时,先调用~B(),释放B类对象的内存,再释放A类对象的内存。
    base *p = new derive;
    delete p
    

      如果~base()不是虚函数,那么delete p只释放了base类的内存,而没有释放derive的内存。所以通常给一个基类提供一个虚函数的析构函数,即是它不需要析构函数。

    1. 友元不能是虚函数,因为友元不是类成员
  • 相关阅读:
    推荐一个wpf&sliverlight的图表控件
    数独求解
    WPF中的 CollectionChanged事件通知
    Windows 7 任务栏之缩略图预览(Thumbnail)
    把Google HK设为IE默认的搜索引擎
    F#小记——1. Hello F#
    F#小记——2. 基本数据类型
    使用异步socket的时候需要注意memory spike
    《everytime you kissed me》的中文歌词
    我回来了o(∩_∩)o...
  • 原文地址:https://www.cnblogs.com/h-hg/p/8688789.html
Copyright © 2011-2022 走看看