2008年1月17日星期四

C++基础:重载与覆盖

今天,问了一个同事关于overloading和overriding的区别,但是这位已工作多年的程序员竟然支支吾吾说不清出其中的差别。这点我很吃惊。后来又问了几个人,发现竟然不止一个人没有搞清其中的差别。>


重载(overloading)和覆盖(overriding)其实是八竿子都打不着的两个概念。

重载指在同一个程序块范围中有多于一个函数用相同的函数名但是拥有不同的函数签名。

这句话里面有几个概念。一个是在同一个程序块范围(Scope)里面的才算重载。第二个就是函数签名。不同的函数签名指的是有不同的个数和类型的参数,函数签名不包括返回值。比如:

void foo(int& i);
int foo(int& i);

这两个函数拥有同样的签名,因此不算是重载,而且编译器会报错。

当提供了重载函数,在调用这个函数的时候,编译器会根据一定的规则(参数匹配,自动转换匹配等),来找到最合适的函数调用。重载的意义很重要,他提供了一种设计手段,使得相同的抽象概念接口可以根据实际情况(参数)来产生多种实现。比如最常用的是操作符重载。当一个自定义类型在语意上应该提供类似自然数的+操作的时候,它可以通过重载这个操作符来获得这种很自然的行为。

T t1,t2;
t1 = t1+t2; // overloading operator +
t1 = t1.Add(t2); // member function.

上面两种表达法中,很明显第一种更自然。

覆盖(overriding)指派生类(derived class) 提供了和基类(Base Class)中相同名字和签名的虚函数。那么这里也有两个概念。一个是覆盖仅发生在继承关系中,这和重载产生作用的范围不同。另外一个是覆盖只能针对于父类的虚函数,不是实函数也不是纯虚函数。看下面的代码

class Base{
public:
//....
virtual void foo(int);
void foo(double);
//....
}

class Derived{
public:
//...
void foo(int);
void foo(double);
//...
}

在这段代码中,基类Base中有两个函数,这两个函数是重载函数。在派生类中,第一个函数覆盖了基类中的虚函数。而后面一个foo(double)并不会覆盖基类中的同样签名的函数。因为在基类中那个函数不是虚函数。另外在派生类中的两个foo同样是重载函数。派生类中的foo(double)不会重载基类中的函数,因为他们在不同的scope中。

重载和覆盖的话题并不至于此,还有在template中的让人混淆的用法,但是千变不离其宗,记住这两个概念的作用域和发生的上下文即可进行判断。

没有评论: