zoukankan      html  css  js  c++  java
  • 转:C++的重载(overload)与重写(override)

    C++ override overload 的区别

     override是指在不同作用域,多个函数原型完全一样,而实现却不同的函数。在C++中,经常发生在类的继承中。当基类中的某个方法是virtual或pure virtual函数时(当然访问权限必须是public或protected,因为从C++设计思想上来讲private的函数不会是virtual的,呵呵),其子类中对该方法的重新实现就属于override。使用时,可以通过基类的指针或者引用所指向的具体对象来确定调用的是哪个方法,从而实现函数的多态。对于基类中的非virtual类型的成员函数,若其子类中也声明了与该函数名称相同的函数,那么基类中的该函数(也许是一系列函数,如果该函数在基类中有重载的话,呵呵)将被隐藏,可以通过域解析操作符来调用。不过按照C++的设计思想来说呢,基类中的非virtual类型的成员函数,是不需要在子类中进行修改的,所以如果在子类中出现了这种隐藏的情况,说明应该将基类中的该函数改成virtual类型的,然后就是override了,哈哈!
     
            而overload则是指在相同作用域,多个函数具有相同的名字,但参数的数目和类型各不相同(当然相同数目和类型,如果顺序不同也是可以的,哈哈),因为函数重载的机制是在C++中函数的签名与其参数有关,而不像C中,只与函数名有关。
     
             总之,override与overload的一个最大的区别就是作用域不同,以及函数原型是否相同,呵呵。如果使用关键字来总结的话,就是,override:作用域不同,继承,重写,virtual,多态,基类指针/引用来访问;overload:作用域相同,重载。
     

    C++的重载(overload)与重写(override)

    C++的重载(overload)与重写(override)

    成员函数被重载的特征:
    (1)相同的范围(在同一个类中);
    (2)函数名字相同;
    (3)参数不同;
    (4)virtual关键字可有可无。

    重写是指派生类函数重写基类函数,是C++的多态的表现,特征是:
    (1)不同的范围(分别位于派生类与基类);
    (2)函数名字相同;
    (3)参数相同;
    (4)基类函数必须有virtual关键字。

    示例中,函数Base::f(int)与Base::f(float)相互重载,而Base::g(void)被Derived::g(void)重写。

    复制代码
    #include <iostream>
    using namespace std;
    
    class Base
    {
    public:
        void f(int x){ cout << "Base::f(int) " << x << endl; }
        void f(float x){ cout << "Base::f(float) " << x << endl; }
        virtual void g(void){ cout << "Base::g(void)" << endl;}
    };
    
    class Derived : public Base
    {
    public:
        virtual void g(void){ cout << "Derived::g(void)" << endl;}
    };
    
    int main()
    {
        Derived  d;
        Base *pb = &d;
        pb->f(42);        // Base::f(int) 42
        pb->f(3.14f);     // Base::f(float) 3.14
        pb->g();          // Derived::g(void)
    
        return 0;
    }
    复制代码

    令人迷惑的隐藏规则

    本来仅仅区别重载与重写并不算困难,但是C++的隐藏规则(遮蔽现象)使问题复杂性陡然增加。这里“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
    (1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏。
    (2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏。
    这种隐藏规则,不仅仅是表现在对成员函数上,对同名的data member也是如此。

    示例程序中:
    (1)函数Derived::f(float)重写了Base::f(float)。
    (2)函数Derived::g(int)隐藏了Base::g(float)。
    (3)函数Derived::h(float)隐藏了Base::h(float)。

    复制代码
    #include <iostream>
    using namespace std;
    
    class Base
    {
    public:
        virtual void f(float x){ cout << "Base::f(float) " << x << endl; }
        virtual void g(float x){ cout << "Base::g(float) " << x << endl; }
        void h(float x){ cout << "Base::h(float) " << x << endl; }
    };
    
    class Derived : public Base
    {
    public:
        virtual void f(float x){ cout << "Derived::f(float) " << x << endl; }
        virtual void g(int x){ cout << "Derived::g(int) " << x << endl; }
        void h(float x){ cout << "Derived::h(float) " << x << endl; }
    };
    
    int main()
    {
        Derived  d;
        Base *pb = &d;
        Derived *pd = &d;
    
        // Good : behavior depends solely on type of the object
        pb->f(3.14f); // Derived::f(float) 3.14
        pd->f(3.14f); // Derived::f(float) 3.14
    
        // Bad : behavior depends on type of the pointer
        pb->g(3.14f); // Base::g(float) 3.14 (surprise!)
        pd->g(3.14f); // Derived::g(int) 3
    
        // Bad : behavior depends on type of the pointer
        pb->h(3.14f); // Base::h(float) 3.14  (surprise!)
        pd->h(3.14f); // Derived::h(float) 3.14
    
        return 0;
    }
    复制代码

    另一个关于虚函数很微妙的错误情况:参数相同,但是基类的函数是const的,派生类的函数却不是。

    复制代码
    #include <iostream>
    using namespace std;
    
    class Base
    {
    public:
        virtual void f(float x){ cout << "Base::f(float) " << x << endl; }
    };
    
    class Derived : public Base
    {
    public:
        virtual void f(float x) const { cout << "Derived::f(float) " << x << endl; }
    };
    
    int main()
    {
        Derived  d;
        Base *pb = &d;
        Derived *pd = &d;
    
        // Bad : behavior depends solely on type of the object
        pb->f(3.14f); // Base::f(float) 3.14
        pd->f(3.14f); // Derived::f(float) 3.14
    
        return 0;
    }
    复制代码

    (1)一个函数在基类申明一个virtual,那么在所有的派生类都是是virtual的。
    (2)一个函数在基类为普通函数,在派生类定义为virtual的函数称为越位,函数行为依赖于指针/引用的类型,而不是实际对象的类型。

    复制代码
    #include<iostream>
    using namespace std;
    
    class Base
    {
    public:
        void f(){ cout << "Base::f() " << endl; }
        virtual void g(){ cout << "Base::g() " << endl; }
    };
    
    class Derived : public Base
    {
    public:
        virtual void f(){ cout << "Derived::f() " << endl; }
        void g(){ cout << "Derived::g() " << endl; }
    };
    
    class VirtualDerived : virtual public Base
    {
    public:
        void f(){ cout << "VirtualDerived::f() " << endl; }
        void g(){ cout << "VirtualDerived::g() " << endl; }
    };
    
    int main()
    {
        Base *d = new Derived;
        Base *vd = new VirtualDerived;
    
        d->f(); // Base::f() Bad behavior
        d->g(); // Derived::g()
    
        vd->f(); // Base::f() Bad behavior
        vd->g(); // VirtualDerived::g()
    
        delete d;
        delete vd;
    
        return 0;
    }
    复制代码

    《Effective C++》条款: 决不要重新定义继承而来的非虚函数。说明了不能重新定义继承而来的非虚函数的理论依据是什么
    以下摘自《Effective C++》:
    公有继承的含义是 "是一个","在一个类中声明一个非虚函数实际上为这个类建立了一种特殊性上的不变性"。如果将这些分析套用到类B、类D和非虚成员函数B::mf,那么:

    (1)适用于B对象的一切也适用于D对象,因为每个D的对象 "是一个" B的对象。
    (2)B的子类必须同时继承mf的接口和实现,因为mf在B中是非虚函数。

    那么,如果D重新定义了mf,设计中就会产生矛盾。如果D真的需要实现和B不同的mf,而且每个B的对象(无论怎么特殊)也真的要使用B实现的mf,那么每个D将不 "是一个" B。这种情况下,D不能从B公有继承。相反,如果D真的必须从B公有继承,而且D真的需要和B不同的mf的实现,那么,mf就没有为B反映出特殊性上的不变性。这种情况下,mf应该是虚函数。最后,如果每个D真的 "是一个" B,并且如果mf真的为B建立了特殊性上的不变性,那么,D实际上就不需要重新定义mf,也就决不能这样做。

    不管采用上面的哪一种论据都可以得出这样的结论:任何条件下都要禁止重新定义继承而来的非虚函数。

  • 相关阅读:
    架构的本质
    gulp 在 angular 项目中的使用
    Ionic 安装部署
    REST API 基于ACCESS TOKEN 的权限解决方案
    ionic 运行过程中动态切换API服务器地址
    ionic 实现双击返回键退出应用功能
    TPS和QPS是什么,他们的区别是什么
    redis和mySql的数据同步的解析
    mySql的UDF是什么
    export导出.xls时,在火狐的情况下出现表名乱码的情况的解决方案
  • 原文地址:https://www.cnblogs.com/kira2will/p/4263381.html
Copyright © 2011-2022 走看看