重载、重整与重写是C++中几个比较容易混淆的概念。实际上它们的意思完全不一样。在面试环节中,考官很容易抓住这个难点,找你问问题。而唯有对其深刻理解和掌握好了,才能流畅的回答考官的问题。
重载,是指允许存在许多个同名函数,而这些函数的参数不同。
重载的规则有:被重载的方法必须改变参数列表;被重载的方法可以改变返回类型,但是不能通过函数返回类型来区分重载的不同函数。
在C++中,重载是通过对函数进行“重整”(name mangling)实现的。其实现的方法为:编译器根据函数的不同的参数表,对同名函数的名称做修饰,对于编译器而言,这些同名函数就成了不同的函数,他们的调用地址在编译期就绑定了。每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。例如有这样一个函数:
int func(int x, int y);
int func(char x, int y);
经过编译后,函数变成为:
@func_int4int4
@func_char1int4
从此处可以看出,如果把返回值也做为一个可区别的特征话,即:
int func(int x, int y);
char func(int
x, int y);
那么在做如下调用的时候:
int a, b;
func(a, b);
由于在语句中根本没包含返回值,就无法区别不同的重载函数了。
此外,如果在C++程序中要引用C语言编写的库和函数,必须加上extern “C”说明。这是为什么呢?
在C语言中,由于没有重载机制,所以C编译器并不对函数进行重振,即不加相应的修饰符。如果不加extern “C”,那么C++就会对其中的函数进行重整,从而找不到相关的正确的函数体。只有加了之后,C++才不会对其进行重整,因此才能按照C的格式调用,使用对应的C代码。
在继承关系中,如果派生类重新定义的一个方法,如果其名称,返回类型及参数签名与基类中的某个方法的名称,返回类型及参数签名相匹配,那么派生类的方法覆盖了基类的方法,叫方法覆盖或方法重写。
1)派生类的方法名称,参数签名和返回类型必须与基类的方法名称,参数签名和返回类型一致。
2)派生类的方法不能缩小基类方法的访问权限,只能扩大不能缩小。
3)派生类方法不能抛出比基类方法更多的异常,派生类方法抛出的异常必须与基类相同或抛出的异常类为基类抛出异常类的派生类。
4)基类的静态方法不能被派生类覆盖为非静态方法.派生类可以定义与基类的静态方法相同的静态方法,以便派生类隐藏基类的静态方法.同样派生类定义的静态方法也必须满足方法覆盖的约束,方法名,方法的参数签名,返回类型一致。不能缩小基类的访问权限,不能抛出比基类方法更多的异常。
5)基类的非静态方法不能被派生类覆盖为静态方法。
方法重载和方法重写的相同点和不同点:
相同点:都要求方法名称相同
不同点:
方法重载发生在同一个类的内部的方法内,而方法重写发生在继承关系中,派生类重写了基类的方法
方法重载不要求方法的返回类型一致与否 而方法重写要求返回类型必须一致
方法重载要求方法的参数签名必须不一致,而方法重写要求方法的参数签名必须一致
方法重载对方法的访问权限和抛出异常没有限制,而方法重写对方法的访问权限和抛出异常有限制
决不要重新定义继承而来的非虚函数。为什么呢?现在来分析如下例子:
class B
{
public:
void mf();
...
};
class D: public B
{
...
};
D x;
// x是类型D的一个对象
B *pB = &x;
// 得到x的指针
pB->mf();
// 通过指针调用mf
和下面这么做的执行行为不一样:
D *pD = &x;
// 得到x的指针
pD->mf();
// 通过指针调用mf
class D: public B
{
public:
void mf();
// 隐藏了B::mf; 参见条款50
...
};
D x;
B *pB = &x;
pB->mf();
D *pD = &x;
pD->mf();
如果写类D时重新定义了从类B继承而来的非虚函数mf,D的对象就可能表现出精神分裂症般的异常行为。也就是说,D的对象在mf被调用时,行为有可能像B,也有可能像D,决定因素和对象本身没有一点关系,而是取决于指向它的指针所声明的类型。引用也会和指针一样表现出这样的异常行为。
Copyright 2011-2020 © MallocFree. All rights reserved.