区分重载(Overloading)、重写(Overriding)与隐藏(Hiding)

在面向对象(OO)的世界中存在着三个十分容易混淆的概念:重载(Overloading)、重写(Overriding)、隐藏(Hiding)。

1.重载

重载是指同一作用域的不同函数使用相同的函数名,但是函数的参数个数或类型不同。

重载在C中就已经存在了,正如我们所熟悉的abs函数一样,如下所示:

double abs(double); 
int abs(int); 
abs(1);         // call abs(int); 
abs(1.0);       // call abs(double);

重载函数就是在一个类空间里具有相同名字、不同参数的一些函数。比如下面类Maxer中的Max函数:

class Maxer {
public:

    void Max(int a, int b);

    void Max(double a, double b);

    void Max(double a, double b, double c);

    ...// other code
};

但是,如果用派生类newMaxer继承基类Maxer:

class newMaxer : public Maxer {
public:

    void Max(int a, double b);

    ...// other code
};

派生类newMaxer中的Max函数并不是基类Maxer中Max函数的重载兄弟,因为它们分属于不同的作用域。所以当写下如下代码时,编译器会报错:

newMaxer newMax; 
newMax.Max(1, 3);     // 编译报错

这是因为在派生类的作用域中没有找到Max(int, int)的函数定义,基类Maxer中的Max被派生类中的Max(int, double)掩盖了,于是就出现了“参数不匹配”的错误提示。如果想让它们兄弟四个构成重载,需要将基类中的Max函数声明引入到派生类的作用域中,如下所示:

class newMaxer : public Maxer {
public:

    using Maxer::Max;
    void Max(int a, double b);

    ...// other code
};

2.重写

重写是指在派生类中对基类中的虚函数重新实现,即函数名和参数都一样,只是函数的实现体不一样。重写是我们十分熟悉的一个操作,它与虚函数的实现息息相关。

这里涉及两个关键要素:派生类和虚函数,如下所示:

class Student {
public:

    Student(){}

    ~Student(){}

    virtual void Show() {
        std::cout<<"Student..."<<std::endl;
    }
    
};

class CollegeStudent : public Student {
public:

    CollegeStudent(){}

    ~CollegeStudent(){}

    virtual void Show() {
        std::cout<<"CollegeStudent..."<<std::endl;
    }
}

但是重写有几点必须注意:

  • 函数的重写与访问层级(public、private、protected)无关。
class CollegeStudent : public Student {
public:

    CollegeStudent(){}

    ~CollegeStudent(){}
    
private:

    virtual void Show() {
        std::cout<<"CollegeStudent..."<<std::endl;
    }
}

上述派生类中的Show与基类的访问层级不同,但还是成功地实现了对该函数的特殊定制。

  • const可能会使虚成员函数的重写失效。

常量成员函数与一般的成员函数在函数签名中是不同的,其常量属性是函数签名中的一部分。

class CollegeStudent : public Student {
public:

    CollegeStudent(){}

    ~CollegeStudent(){}
    
    virtual void Show()const {
        std::cout<<"CollegeStudent..."<<std::endl;
    }
}

因为具有不同的函数签名,所以派生类中的Show函数并没有重写基类中的Show函数。

  • 重写函数必须和原函数具有相同的返回类型。

因为函数的返回类型不是函数签名的一部分,所以若派生类重写了基类类型中对应的函数,那么它们必须有相同的返回类型。如果返回值不同,编译器会抛出“重写虚函数返回类型有差异”的错误警示,如下所示:

class CollegeStudent : public Student {
public:

    CollegeStudent(){}

    ~CollegeStudent(){}
    
    virtual bool Show() {
        std::cout<<"CollegeStudent..."<<std::endl;
    }
}

该规则存在一种例外情形,称为“协变返回值类型”。协变的返回值必须是子类或是父类的指针或是引用,如下所示:

class CollegeStudent : public Student {
public:

    CollegeStudent(){}

    ~CollegeStudent(){}
    
    CollegeStudent& Show() {
        std::cout<<"CollegeStudent..."<<std::endl;
    }
}

需要注意的是,如果有返回值,返回值必须是子类或父类的引用或指针,如果父类的返回值是引用,那么子类返回值也是引用;如果父类返回值是指针,那么子类返回值也是指针。否则,编译将不通过。

3.隐藏

隐藏是指派生类中的函数屏蔽基类中具有相同名字的非虚函数。所以它的两个重要要素就是派生类和非虚函数。

在调用一个类的成员函数时,编译器会沿着类的继承链逐级地向上查找函数的定义,如果找到就停止。如果一个派生类和一个基类有一个同名函数,由于派生类在继承链中处于下层,编译器则最终会选择派生类中的函数。如此一来,基类的同名成员函数就会屏蔽隐藏,编译器的函数查找也就到达不了基类中。

还是采用前面的newMaxer类中的Max函数来说明这一问题,如下所示:

class Maxer {
public:

    void Max(int a, int b);

    void Max(double a, double b);

    void Max(double a, double b, double c);

    ...// other code
};

class newMaxer : public Maxer {
public:

    bool Max(int a, int b);
    void Max(int a, double b);

    ...// other code
};

当编译器在继承链中查找到Max函数时,派生类中的Max函数阻止了它向上寻找,隐藏了基类中的Max。

4.总结

最后,列出一张简单的表格让大家可以对这三者有个清晰的理解。

关系 作用域 有无virtual 函数名 参数类型 返回值类型
重载 相同 可有可无 相同 不同 可同可不同
重写(覆盖) 不同 相同 相同 相同(协变)
隐藏(重定义) 不同 可有可无 相同 可同可不同 可同可不同

个人主页:

www.codeapes.cn

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,122评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,070评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,491评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,636评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,676评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,541评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,292评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,211评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,655评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,846评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,965评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,684评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,295评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,894评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,012评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,126评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,914评论 2 355

推荐阅读更多精彩内容