final/override控制

重写的概念

一个类A中声明的虚函数fun在其派生类中再次被定义,且B中的函数根A中fun的原型一模一样(函数名,参数列表,返回值等一样),那么我们称B重写(override)了A的fun函数。对于任何B类型的变量,调用成员函数fun都是调用B重写的版本。如果同时A的派生类C,没有重写A的fun函数,C类型的变量在调用成员函数fun则会调用A中的版本。这在C++中就实现多态。

final用法

基类A中的成员函数fun被声明为virtual的,那么对于其派生类B而言,fun总是能够被重载的(除非被重写了)。又时候我们并不想fun在B类型派生类中被重载,C++98无法对此限制。

#include <iostream>
using namespace std;
class MathObject
{
public:
    virtual double Arith() = 0;
    virtual void Print() = 0;
};
class Printable : public MathObject
{
public:
    double Arith() = 0;
    void Print() //在C++98中不能阻止该接口被重写
    {
        cout << "output is:" << Arith() << endl;
    }
    void Print(int a)
    {
        cout << a << "output is:" << Arith() << endl;
    }
};
class Add : public Printable
{
public:
    Add(double a, double b) : x(a), y(b) {}
    double Arith()
    {
        return x + y;
    }
private:
    double x, y;
};

class Mul : public Printable
{
public:
    Mul(double a, double b, double c) : x(a), y(b), z(c) {}
    double Arith()
    {
        return x * y * z;
    }
private:
    double x, y, z;
};

在上述代码中,基类MathObject定义了两个接口:Arith和Print。类Printable继承MathObject并实现了Print接口。接下来,Add和Mul继承了Printable,可以使用MathObject的接口和Printable的Print的实现。倘若Printable和Add有两个不同的程序员编写,如果Add的编写者重载了Print函数,那么统一的风格的打印方式将不复存在。
final的关键字的作用是使派生类不可覆盖它所修饰的虚函数。

struct Object{
virtual void fun() =0;
};
struct Base:public Object{
	void fun() final;  //声明为final
};
struct Derived : public Base{
	void fun();    //编译报错
};

再上述代码中,派生于Object的Base类从写了Object的fun接口,并本类的fun函数声明为final的。那么再派生于Base的Derived类对接口的重写则会导致编译错误。
基类的虚函数也可以使用final进行修饰。被final修饰的函数无法被重写,则失去了虚函数的意义。如果不想成员函数被重写,可直接将改函数定义为非虚函数。final通常只在继承关系的“中途”终止派生类重写中有意义。

override用法

C++重写对于基类声明为virtual的函数,在派生类中的重写函数则不需要再声明为virtual,即使被声明也会被编译器忽略。因此在C++的虚函数会有“跨层”,没有在父类中声明为virtual的函数可能是祖先的虚函数接口。
在C++11中为了帮助程序员写继承结构复杂的类型,引入虚函数描述符override,若派生类在虚函数声明时使用了override描述符,该函数必须重写其基类中的同名函数,负责代码无法通过编译。

struct Base
{
    virtual void Turing() = 0;
    virtual void Dijkstra() = 0;
    virtual void VNeumann(int g) = 0;
    virtual void DKnuth() const;
    void Print();
};
struct DerivedMid : public Base
{
};
struct DerinedTop : public DerivedMid
{
    void Turing() override;
    void Djikstra() override;       //拼写错误,编译错误
    void VNeumann(double g) override;  //参数类型错误,编译错误
    void DKnuth()  override ;           //常量性错误,编译错误
    void Print() override;               //非虚函数重写错误,编译错误
};

如果没有override修饰符号,代码出错的可能性较大,且排查错误较为麻烦。override修饰符可以让编译器做一些辅助的检查。

总结

final/override在C++98的标准中,可以定义为正常变量名,只有final/override出现在函数后时才是能够控制继承和派生的关键字。此种设计对C++98的代码具有很好的兼容性。在c++11标准中尽量避免使用final/override作为变量名称。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值