在C++中, 函数的覆盖(override,注:大多数文章及书本中把override也称为重载,我认为不妥,在此借用Delphi术语“覆盖”与“重载(overload)”区分)是在基类中用virtual关键字来修饰,称为虚函数,如果派生类中对基类中的虚函数进行覆盖,那么使用指向派生类的父类指针调用此函数,其实运行的是派生类中的版本。
例如:
#include <iostream>
using namespace std;
class a {
public:
virtual void Hello() {cout<<"Class a"<<endl;}
};
class b : public a {
public:
void Hello() {cout<<"Class b"<<endl;}
};
int main(int argc, char* argv[])
{
a *ia = new a;
ia->Hello(); //输出"Class a"
a *ib = new b;
ib->Hello(); //输出"Class b"
delete ia;
delete ib;
return 0;
}
而在CBuilder和Delphi中增加了__property关键字,称为属性,它可以指定一个读方法和写方法或者直接进行变量存取,例如:
__property AnsiString Name = {read=FName, write=SetName}; //TComponent 的Name属性声明。
(根据CBuilder/Delphi约定,直接存取的变量名用字母F开头,读方法用Get+属性名,写方法用Set+属性名)
如果你从TComponent派生一个新类TMyComp,重新定义了Name属性,有这么几种可能碰到的情况:
1. 为了处理设置Name时的行为,你重新定义了写方法SetName,而且内部也定义了一个叫FName的变量。
__property AnsiString Name = {read=GetName, write=SetName}
如果这样做,将得不到覆盖的效果,因为第一种情况下,对Name的赋值将是对TMyComp内部的FName的赋值,而不是对基类TComponent中的FName赋值,补救的办法是去除TMyComp的FName变量并且把基类的FName放于protected段使派生类可以直接访问(这样做常常会使人莫名其妙,因为在派生类对FName赋值却没有发现FName的定义); 2. 重新声明了属性并更改读方法
而第二种情况下声明了GetName读方法,由于基类TComponent中并没有声明虚函数GetName,所以这两种情况下如果这样写:
TComponent *MyComp = new TMyComp(NULL);
MyComp->Name = "Hello";
ShowMessage(MyComp->Name);
最后显示的是空字符而不是你想要的"Hello"。所以,如果基类属性的声明中读写方法中有一个是直接变量赋值或者是非虚函数方法。那么为了达到覆盖的效果,它的派生类中将不能重新对这个属性进行覆盖定义(但可以降低限制等级或者更改默认值或者设为只读),除非你不在乎它失去函数覆盖这个重要的性能。
(一点小经验,如果有不对的地方请发表你的意见)