重写(覆盖)和隐藏 重载区别 为什么需要隐藏

重写(覆盖):override
是指派生类中存在重新定义的函数。
(1)重写是指派生类函数覆盖基类函数。
(2)重写的特征:
①不同的作用域(分别位于派生类与基类);
②函数名字相同;
③参数相同;
④基类函数必须是虚函数;(重写的基类中被重写的函数必须有virtual修饰)
⑤重写函数必须和原函数具有相同的返回类型;
⑥const可能会使虚成员函数的重写失效。
其函数名,参数列表,返回值类型,所有都必须同基类中被重写的函数一致。
只有函数体不同(花括号内),派生类调用时会调用派生类的重写函数,不会调用被重写函数。
class Base {
public:
    virtual void fun(int a)  {
        cout<<"base::fun(int)"<<endl;
    }
};
 
class Derive:public Base {
public:
    void fun(int a) {
        cout<<"Derive::fun(int )"<<endl;
    }
};
int main(int argc, char *argv[])
{
    Derive pd;
    pd.fun(1);
 
    return 0;
}
 
隐藏: (隐藏指的是子类把父类的属性或者方法隐藏了,即将子类强制转换成父类后,调用的还是父类的属性和方法)
(1)隐藏,是指派生类的函数屏蔽屏蔽了与其同名的基类函数。
(2)特征:
①如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数都将被隐藏。(注意与重载区分)
②如果派生类的函数与基类的函数同名,且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏。(注意与覆盖区分)
static 不影响
是指派生类的函数屏蔽了与其同名的基类函数,注意只要同名函数,不管参数列表是否相同,基类函数都会被隐藏。
class Base {
public:
    void fun(double a, int b)  {
        cout<<"base::fun(double, int)"<<endl;
    }
};
 
class Derive:public Base {
public:
    void fun(int a) {
        cout<<"Derive::fun(int )"<<endl;
    }
};
int main(int argc, char *argv[])
{
    Derive pd;
    pd.fun(1);  //Derive::fun(int )
    pd.fun(0.01, 2);  //error C2660: “Derive::fun”: 函数不接受 2 个参数
 
    Base *fd = &pd;
    fd->fun(1.0,1);  //Base::fun(double ,int);
    fd->fun(1);  //error
    return 0;
}
 
例子:
#include<iostream>
 
using namespace std;
class Base {
public:
    virtual void f(float x) {
        cout<<"Base::f(float)"<<x<<endl;
    }
    void g(float x) {
        cout<<"Base::g(float)"<<x<<endl;
    }
    static void h(float x) {
        cout<<"Base::h(float)"<<x<<endl;
    }
};
class Derive:public Base {
public:
    virtual void f(float x) {
        cout<<"Derive::f(float)"<<x<<endl;
    }
    void g(int x) {
        cout<<"Derive::g(int)"<<x<<endl;
    }
    static void h(float x) {
        cout<<"Derive::h(float)"<<x<<endl;
    }
};
int main(int argc, char *argv[])
{
    Derive d;
    Base *pb = &d;
    Derive *fd = &d;
 
    pb->f(3.14f);  //Derived::f(float) 3.14
    fd->f(3.14f);  //Derived::f(float) 3.14
 
    pb->g(3.14f); //Base::g(float) 3.14
    fd->g(3.14f); //Derived::g(int) 3
 
    pb->h(3.14f); //Base::h(float) 3.14
    fd->h(3.14f); //Derived::h(float) 3.14
    return 0;
}
(1)函数Derived::f(float)覆盖了Base::f(float)。
(2)函数Derived::g(int)隐藏了Base::g(float),而不是重载。
(3)函数Derived::h(float)隐藏了Base::h(float),而不是覆盖。
 
重载
(1)相同的作用域(同一个类中);
(2)函数名字相同;
(3)参数不同;
(4)virtual关键字可有可无。
class Base {
public:
    virtual void disp(int a) {
        cout<<"Base::disp()"<<endl;
    }
 
    void disp(int a, int b) {
        cout<<"Base::disp(int,int)"<<endl;
    }
};
 
为什么需要隐藏
假若没有隐藏机制,改变基类实现将有可能影响到子类已经正常工作的代码出现未预料的行为,这不是我们希望看到的。
因此,C ++采用命名隐藏机制来避免引入这一麻烦。
#include<iostream>
using namespace std;
 
class B {
public:
        int f(int i) { cout << "f(int): "; return i+1; }
        // ...
};
 
class D : public B {
public:
        double f(double d) { cout << "f(double): "; return d+1.3; }
        //use using to make f(int) visible
        using B::f; // make every f from B available
};
 
int main()
{
        D* pd = new D;
        cout << pd->f(2) << endl;
        cout << pd->f(2.3) << endl;
}
这次运行结果为:
f(int): 3
f(double): 3.6
为什么需要隐藏
#include <cassert>
struct Base {
  // ...
};
 
struct Derived: Base {
   bool foo(long) {return true;}
};
 
int main() {
  Derived d;
  assert(d.foo(1));
}
上述程序运行正常,子类调用foo(long)函数,一切Ok.
对于基类来讲,作者并不一定知道,子类的实现,假若作者添加了一个函数,代码如下:
struct Base {
  // ...
  bool foo(int) {return false;}
};
 
struct Derived: Base {
   using Base::foo; // simulate if name hiding not working
   bool foo(long) {return true;}
};
 
int main() {
  Derived d;
  assert(d.foo(1)); // will failed then print error info
}
假设没有命名隐藏机制,上述代码中我们使用using Base::foo来模拟没有命名隐藏机制,则上述代码运行时,由于调用基类Base::foo(int)而导致assert失效,出错.
也就是说:
假若没有隐藏机制,改变基类实现将有可能影响到子类已经正常工作的代码出现未预料的行为,这不是我们希望看到的.
因此,C++采用命名隐藏机制来避免引入这一麻烦.
当然,改变基类也并不一定会影响子类正常运行,这里只是举出一个特例加以说明. 
posted @ 2019-07-25 16:30  小兵07  阅读(429)  评论(0编辑  收藏  举报