C++开发中级
1011 浏览 5 years, 10 months
1.2 重写方法
版权声明: 转载请注明出处 http://www.codingsoho.com/重写方法
从某个类继承的主要原因是为了添加或者替换功能。Sub类定义在父类基础上添加了功能,提供了额外的someOtherMethod()方法。另一个方法someMethod()从Super继承而来,这个方法的行为在子类中与在超类中相同。在许多情况下您可能想要替换或者重写某个方法来修改类的行为。
1.将所有方法都设置为virtual,以防万一
在C++中,重写(override)方法有一点别扭,因为必须使用关键字virtual。只有在超类中声明为virtual的方法才能被子类正确地重写。virtual关键字出现在方法声明的开头,下面显示了Super的修改版本:
class Super
{
public:
Super();
virtual void someMethod();
protected:
int mProtectedInt();
private:
int mPrivateInt;
};
virtual关键字有些微妙,经常被当作语言的设计不当部分。经验表明最好将所有方法都设置为virtual。这样一来,就不必担心重写方法是否可以运行,这样做唯一的缺点是对性能具有轻微的影响。virtual关键字会贯穿本章,在后面将进一步讨论性能。
即使Sub类不大可能被扩展,最好还是将这个类的方法设置为virtual,以防万一
class Sub : public Super
{
public:
Sub();
virtual void someOtherMethod();
};
从根据经验,为了避免遗漏virtual关键字引发的问题,可将所有方法设置为virtual(包括析构函数,但不包括构造函数)。
2.重写方法的语法
为了重写某个方法,需要在子类定义中重新声明这个方法,就像在超类中声明的那样,并在子类的实现文件中提供新的定义。例如,Super类包含了一个名为someMethod()的方法,在Super.cpp中提供的someMethod()定义如下:
void Super::someMethod()
{
cout << "This is Super's version of someMethod()." << endl;
}
注意在方法定义中不需要重复使用virtual关键字。
如果您希望在Sub类中提供someMethod()的新定义,首先应该在Sub类定义中添这个方法,如下所示:
class Sub : public Super
{
public:
Sub();
virtual void someMethod(); //Overrides Super's someMethod()
virtual void someOtherMethod();
};
someMethod()方法的新定义与Sub的其他方法一并在Sub.cpp中给出:
void Sub::someMethod()
{
cout << "This is Sub's version of someMethod()." << endl;
}
一旦将方法或者析构函数标记为virtual,它们在所有子类中就一直是virtual,即使在子类中删除了virtual关键字也同样如此。例如,在下面的Sub类中,someMethod()仍然是virtual,并且可以被Sub的子类重写,因为在Super类中已经将其标记为virtual 。
class Sub : public Super
{
public:
Sub();
void someMethod(); //Overrides Super's someMethod()
virtual void someOtherMethod();
};
3.客户对于重写方法的看法
经过前面的改动之后,其他代码仍然可以用先前的方法调用someMethod(),可以用Super类的对象也可以使用Sub类的对象调用这个方法。然而,现在someMethod()的行为将根据对象所属类的不同而变化。
例如,下面的代码与先前一样可以运行,调用Super版本的someMethod():
Super mySuper;
mySuper.someMethod(); // call Super's version of someMethod().
这段代码的输出为
This is Super's version of someMethod().
如果声明一个Sub类的对象,将自动调用子类版本:
Sub mySub;
mySub.someMethod(); // call Sub's version of someMethod().
这段代码的输出为:
This is Sub's version of someMethod().
Sub类对象的其他方面维持不变。从Super继承的其他方法仍然保持Super提供的定义,除非在Sub中显式地将这些方法重写。
您在前面己经知道,指针或者引用可以指向某个类的对象或者子类的对象。对象本身“知道”自己所属的类,因此只要这个方法为声明为virtual,就会自动调用对应的方法。例如,如果您有一个对Super引用,而实际引用的是Sub对象,调用someMethod()实际上会调用子类版本,如下面的示例所示。
如果在超类中省略了virtual关键字,重写功能将无法正确运行。
Sub mySub;
Super& ref = mySub;
ref.someMethod(); // call Sub's version of someMethod().
记住,即使超类的引用或者指针知道这实际上是一个子类,也无法访问没有在超类中定义的子类方法或者成员。下面的代码无法编译,因为Super引用中没有一个称为someOhterMethod()的方法:
Sub mySub;
Super & ref=mySub;
mySub.someOtherMethod(); // This is fine
ref.someOtherMethod(); //BUG
非指针非引用对象无法正确处理子类的特征信息。您可以将Sub转换为Super,或者将Sub赋值给Super,因为Sub是一个Super。然而,此时这个对象将遗失子类的所有信息:
Sub mySub;
Super assignedObject = mySub; //assign a Sub to a Super
assignedObject.someMethod(); // call Super's version of someMethod()
为了记住这个看上去有点奇怪的行为,可以考虑对象在内存中的状态。将Super对象当作占据了一块内存的盒子。Sub对象是稍微大一点的盒子,因为它拥有Super的一切,还添加了一点内容。对于Sub的引用或者指针,这个盒子并没有变—只是可以用新的方法访问它。
然而,如果将Sub转换为Super,就会为了适应较小的盒子而扔掉Sub类所有的“特性”。
超类的指针或者引用指向子类对象时,子类保留其重写方法。但是如果通过类型转换将子类对象转换为超类对象,此时会丢失其特征。重写方法以及子类数据的丢失就是所谓的截断(slicing) 。
4.禁用重写(仅限C++11)
C++11允许将方法标记为final,这意味着无法在子类中重写这个方法。试图重写final()
方法将导致编译器错误。考虑下面的Super类:
class Super
{
public:
Super();
virtual void someMethod() final;
}
在下面的Sub类中重写someMethod()会导致编译器错误,因为someMethod()在Super类中被标记为final 。
class Sub : public Super
{
public:
Sub();
virtual void someMethod(); //Error
virtual void someOtherMethod();
}
参考代码 Sub\SubOverride.cpp