很多人无法正确区分函数重载、函数隐藏与函数重写这三个概念,下面将给这三个概念下个定义,并讨论C++与C#中的异同。
重载函数(overloaded function):在相同的作用域中的函数名相同,而参数表不同,即通过函数的参数表而唯一标识并且来区分函数的一种特殊的函数。
在C++和C#中,都不能以返回值区分两个函数。如果两个函数是不同的函数,那么这两个函数的签名不完全相同。函数签名包括函数名、参数类型,参数个数和参数顺序,不包括返回值。可以这么理解:返回值为int和返回值为long 的函数都可以返回0,所以不能仅按返回值区分两个函数。
在继承与多态上,C++中,如果派生类中与基数存在同名函数(只要函数名相同),那么基类中的所有同名函数都会被隐藏。如果要在派生类中调用基类的同名函数,可以采用类名::函数名 的方法。
在C#中,如果派生类中存在与基类函数签名完全相同的函数,那么基类中与之有完全相同的函数签名的函数就会被隐藏,在派生类中应该在该函数前加上修饰符new来告诉编译器我们是故意隐藏基类函数。在派生类中只会隐藏基类中有完全相同的函数签名的函数,其他不同函数签名的同名函数不会被隐藏,可以通过类似于重载函数的方法调用他们。这一点与C++是不同的,C++中,派生类中一旦出现同名函数,则基类中所有同名函数都被隐藏,必须使用类名+::+函数名 来访问基类的同名函数。
重写用在多态性上,派生类对基数的虚函数的重定义称为重写(覆盖)(override)。在C++与C#中,如果基类的函数用virtual修饰,那么派生类中重写该函数时,必须拥有完全相同的函数原型(包括返回值类型、函数名、参数类型、参数个数和参数顺序)。
在C++中,如果基类中的函数有virtual修饰,那么派生类中与之拥有相同函数签名的函数就会被认为是在重写基类的虚函数,而不会被认为是对基类函数的隐藏,这时,派生类中的该函数还要求必须与基类中的函数拥有相同的返回值。
而在C#中,若派生类中如果存在与基类函数签名完全相同的函数,如果在派生类中使用override修饰,则被认为是在重写基类的虚函数,这时又要求派生类中的该函数与基类中的函数要拥有相同的返回值;如果是用new修饰,则被认为是在隐藏基类的函数。只有被标记为virtual和abstract方法才能用override修饰并重写。
在C++中,无论是在类中还是在类外,都可以用采 类名::函数名 的形式访问基类中的函数。而C#中只能在类中使用base.函数名 的形式访问。下面的访问是错误的:
Derived d = newDerived();
d.(base.fun());