C++中的Overload、Override和Overwrite

  在C++语言中有一组基础的概念一直都容易混淆:Overload、Override和Overwrite分别表示什么意思?下面把这三个概念整理一下:

1. Overload(重载)

  重载的概念最好理解,在同一个类声明范围中,定义了多个名称完全相同、参数(类型或者个数)不相同的函数,就称之为Overload(重载)。重载的特征如下:

(1)相同的范围(在同一个类中);
(2)函数名字相同;
(3)参数不同;
(4)virtual 关键字可有可无。

2. Override(覆盖)

  覆盖的概念其实是用来实现C++多态性的,即子类重新改写父类声明为virtual的函数。Override(覆盖)的特征如下:

(1)不同的范围(分别位于派生类与基类);
(2)函数名字相同;
(3)参数列表完全相同;
(4)基类函数必须有virtual 关键字。

3. Overwrite(改写)

  改写是指派生类的函数屏蔽(或者称之为“隐藏”)了与其同名的基类函数。正是这个C++的隐藏规则使得问题的复杂性陡然增加,这里面分为两种情况讨论:

(1)如果派生类的函数与基类的函数同名,但是参数不同。那么此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。那么此时,基类的函数被隐藏(注意别与覆盖混淆)

  借鉴一个网上的例子来看Overwrite(改写)的情况:

C++中的Overload、Override和Overwrite
C++中的Overload、Override和Overwrite
#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;
}
View Code

   在上面这个例子中:

  • 函数Derived::f(float)重写了Base::f(float)。
  • 函数Derived::g(int)隐藏了Base::g(float)。
  • 函数Derived::h(float)隐藏了Base::h(float)。

4. 特殊情况说明

  除了上面讲到的三种情况之外,还有一些比较容易迷惑的地方,例如:

4.1 同名的普通函数与const函数本质上是两个不同的函数,应该等价理解为这两个同名函数的参数是不同的。在派生类中的virtual函数理解上可能会有误解。

  参见如下例子:

C++中的Overload、Override和Overwrite
C++中的Overload、Override和Overwrite
#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;
}
View Code

4.2 基类中定义的virtual虚函数,在继承子类中同名函数自动都属于虚函数,可以不需要virtual关键字。

4.3 如果基类中定义的函数不是virtual,而子类中又将相同函数定义为virtual,则称之为越位,函数行为依赖于指针/引用的类型,而不是实际对象的类型。

  参见如下例子:

C++中的Overload、Override和Overwrite
C++中的Overload、Override和Overwrite
#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;
}
View Code

 5. 针对非虚函数的继承说明

  在《Effective C++》中讲述了这样一个规则:任何条件下都要禁止重新定义继承而来的非虚函数。

  公有继承的含义是 "是一个"(is a),"在一个类中声明一个非虚函数实际上为这个类建立了一种特殊性上的不变性"。如果将这些分析套用到类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,也就决不能这样做。

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

更多相关文章
  • overload, override, overwrite, rewrite 这几个单词常常出如今 C++ 书中,翻阅一些译版后发现并未对 override, overwrite, rewrite 严格区分,大都翻译成重写.事实上查阅英文原版会发现.不同的上下文用不同的单词描写叙述.下面逐个分析: ...
  • abstract     修饰类名为抽象类,修饰方法为抽象方法.如果一个类为抽象类,则这个类智能是其他某个类的基类.抽象方法在抽象类中没有函数体.抽象类中的抽象方法是没有方法体的,继承其的子类必须实现抽象类的抽象方法.      抽象类有如下特征:   抽象类不能实例化 抽象类的派生类必须实现所有抽
  • 很多时候导入android项目在eclipse中会报@Override错误,这是由于java编译器的版本不正确,Java 1.5的编译器默认对父类的方法进行覆盖,采用@Override进行说明:但1.6已经扩展到对接口的方法:所以如果还是以Java 1.5的编译器来编译的话,会出现错误.在eclip ...
  • http://www.cnblogs.com/qlee/archive/2011/07/04/2097055.html 成员函数的重载.覆盖与隐藏成员函数的重载.覆盖(override)与隐藏很容易混淆,C++程序员必须要搞清楚概念,否则错误将防不胜防.8.2.1 重载与覆盖成员函数被重载的特征:( ...
  • overload:重载指的是同一个类中有两个或多个名字相同但是参数不同的方法,(注:返回值不能区别函数是否重载),重载没有关键字. override:过载也称重写是指子类对父类中虚函数或抽象函数的"覆盖"(这也就是有
  • 1.如果父类方法没有加virtual关键字,即不是一个虚方法,则在子类中只能隐藏基类方法,而不能覆盖. 2.如果父类方法加了virtual关键字,即它是一个虚方法,在子类中一样可以隐藏. 3.如果子类实现了父类相同的方法(相同的方法名称及签名),而没有new,在编译时会报警,但编译仍然能够通过! 4
  • 解决MyEclipse中导入项目@Override错误
    做项目的时候,同事那边电脑上编译通过的java代码,或者是网上下载的例子代码,导入project后却是编译不通过,总是@override报错,把@override去掉就好了,有时候@Override出现的地方非常多,要所有删除@Override非常繁琐非常吐血,不能从根本上解决这个问题.网上找了一下 ...
  • 关于Override在JDK1.5和JDK1.6上子类实现接口中方法使用@Override注解编译错误.
    遇到这个问题说来也怪.新开了一个path的工作空间用来打patch.该Eclipse的默认全局的编译版本是1.6.但是唯独其中的一个插件项目的版本是1.5(可能是唯一的一个,不确定,不知道为什么会是这样) 然后这个插件中编译错误,该编译错误就是这个Override注解的使用. 一开始秉承同事告诉我的 ...
一周排行