C++ 函数重载、隐藏与覆盖的区别

代码编译运行环境:VS2017+Debug+Win32


1.函数重载(Function Overload)

1.1 定义

C++ 规定在同一作用域中,同名函数的形式参数(指参数的个数、类型或者顺序)不同时,构成函数重载。

1.2 示例

比如,要从两个变量中返回其中较大的一个值,可以编写如下两个构成重载的函数。

int max(int a,int b)  {
	return a>b?a:b;
};

double max(double a,double b) {
	return a>b?a:b;
}

1.3 注意事项

(1)函数返回值类型与构成函数重载无任何关系;
(2)类的静态成员函数与普通成员函数可以形成重载;
(3)函数重载发生在同一作用域,如类成员函数之间的重载、全局函数之间的重载。

2.函数隐藏(Function Hiding)

2.1 定义

函数隐藏指不同作用域中定义的同名函数构成函数隐藏(不要求函数返回值和函数参数类型相同)。比如派生类成员函数屏蔽与其同名的基类成员函数、类成员函数屏蔽全局外部函数。请注意,如果在派生类中存在与基类虚函数同返回值、同名且同形参的函数,则构成函数重写。

2.2 示例

请仔细研读以下代码。

#include <iostream>
using namespace std;

void func(char* s)  {
    cout<<"global function with name:"<<s<<endl;
}

class A {
    void func() {
        cout<<"member function of A"<<endl;
    }
public:
    void useFunc() {
        //func("lvlv");//A::func()将外部函数func(char*)隐藏
        func();
        ::func("lvlv");
    }
    virtual void print() {
        cout<<"A's print"<<endl;
    }
};

class B:public A {
public:
	//隐藏A::vodi useFunc()
    void useFunc() {          
        cout<<"B's useFunc"<<endl;
    }
    
	//隐藏A::vodi useFunc()
    int useFunc(int i) {      
        cout<<"In B's useFunc(),i="<<i<<endl;
		return 0;
    } 
    
    virtual int print(char* a) {
		cout<<"B's print:"<<a<<endl;
		return 1;
    }
	
	//下面编译不通过,因为对父类虚函数重写时,需要函数返回值类型,函数名称和参数类型全部相同才行
	// virtual int print() 
	//{
		// cout<<"B's print:"<<a<<endl;
    //}
};

int main() {
    A a;
    a.useFunc();
    B b;
    b.useFunc();//A::useFunc()被B::useFunc()隐藏
    b.A::useFunc();
    b.useFunc(2);
    //b.print();//编译出错,A::print()被B::print(char* a)隐藏
    b.A::print();
    b.print("jf");
}

程序执行结果:

member function of A
global function with name:lvlv
B's useFunc
member function of A
global function with name:lvlv
In B's useFunc(),i=2
A's print
B's print:jf

2.3 注意事项

对比函数隐藏与函数重载的定义可知:

(1)派生类成员函数与基类成员函数同名但参数不同。此时基类成员函数将被隐藏(注意别与重载混淆,重载发生在同一个类中)。
(2)函数重载发生在同一作用域,函数隐藏发生在不同作用域。

3.函数覆盖(Function Override)

网上和很多书籍多都会涉及函数覆盖与函数重写的概念,众说纷纭,加大了许多初学者的学习难度,甚至产生误导。事实上,函数覆盖指的就是函数重写。

3.1 定义

重定义派生类中与基类同返回类型、同名和同参数的虚函数,构成虚函数覆盖,也叫虚函数重写。关于返回类型存在一种特殊情况,即返回类型可以不用完全一致,协变返回类型(covariant return type)也可以构成虚函数重写。

3.2 虚函数重写与协变返回类型

如果虚函数返回指针或者引用时(不包括 value 语义),子类中重写的函数返回的指针或者引用是父类中被重写函数所返回指针或引用的子类型(这就是所谓的协变返回类型) [ 4 ] ^{[4]} [4]。看示例代码:

#include <iostream>
using namespace std;

class A{}; 
class B:public A{};

class Base {
public:
	virtual A& show() {
		cout<<"In Base"<<endl;
		return *(new A);
	}
};

class Derived:public Base {
public:
     //返回值协变,构成虚函数重写
	 B& show() {
		cout<<"In Derived"<<endl;
		return *(new B);
	}
};

3.3 注意事项

(1)函数覆盖就是虚函数重写,而不是函数被"覆盖"。
从上面的代码可以看出,函数是不可能被“覆盖”的。有些人可能会错误地认为函数覆盖会导致函数被"覆盖"而"消失",将不能被访问,事实上只要通过作用域运算符::就可以访问到被覆盖的函数。因此,不存在被”覆盖“的函数。

(2)函数覆盖是函数隐藏的特殊情况。
对比函数覆盖和函数隐藏的定义,不难发现函数覆盖其实是函数隐藏的特例。

如果派生类中定义了一个与基类虚函数同名但参数列表不同的非virtual函数,则此函数是一个普通成员函数(非虚函数),并形成对基类中同名虚函数的隐藏,而非虚函数覆盖(重写)。

《C++高级进阶教程》中认为函数的隐藏与覆盖是两个不同的概念。隐藏是一个静态概念,它代表了标识符之间的一种屏蔽现象,而覆盖则是为了实现动态联编,是一个动态概念。但隐藏和覆盖也有联系:形成覆盖的两个函数之间一定形成隐藏。例如,可以对虚函数采用“实调用”,即尽管被调用的是虚函数,但是被调用函数的地址还是在编译阶段静态确定的,那么派生类中的虚函数仍然形成对基类中虚函数的同名隐藏。

参考如下代码,考察虚函数的实调用和虚调用。

#include <iostream>
using namespace std;

class Base {
public:
	virtual void show() {
		cout<<"In Base"<<endl;
	}
};

class Derived:public Base {
public:
	void show() {
		cout<<"In Derived"<<endl;
	}
};

int main() {
	Base b;
	b.show();
	Derived d;
	d.show();          //对函数show()的实调用
	d.Base::show();    //对函数show()的实调用
	Base *pb=NULL;	   
	pb=&d;			   
	pb->show();        //对函数show()的虚调用
	pb->Base::show();  //对函数show()的实调用
}

程序运行结果:

In Base
In Derived
In Base
In Derived
In Base

4.总结

在讨论相关概念的区别时,抓住定义才能区别开来。C++ 中函数重载隐藏和覆盖的区别,并不难,难就难在没弄清定义,被网上各种说法弄的云里雾里而又没有自己的理解。

在这里,牢记以下几点,就可区分函数重载、函数隐藏、函数覆盖和函数重写的区别:
(1)函数重载发生在相同作用域;
(2)函数隐藏发生在不同作用域;
(3)函数覆盖就是函数重写。准确地叫作虚函数覆盖和虚函数重写,也是函数隐藏的特例。

关于三者的对比,李健老师在《编写高质量代码:改善C++程序的150个建议》一书中给出了较为详细的总结,如下表所示:

三者作用域有无virtual函数名形参列表返回值类型
重载相同可有可无相同不同可同可不同
隐藏不同可有可无相同可同可不同可同可不同
重写不同相同相同相同(协变)

参考文献

陈刚.C++高级进阶教程[M].第一版.武汉:武汉大学出版社,2008.P110-112
百度百科.函数隐藏
李健.编写高质量代码:改善C++程序的150个建议.第一版.北京:机械工业出版社,2012.1:122-125
C++基础:函数重写(override)与协变返回类型(covariant return type)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值