c++11 特性 final与override关键字

来源:互联网 发布:spark sql seq 编辑:程序博客网 时间:2024/05/26 12:06

~~~~我的生活,我的点点滴滴!!


C++11中的final/override他们不是关键字,是说明符, 主要用在类继承间限定,虚函数重载的限定等

final 说明符

在通常情况下,一旦在基类A中的成员函数fun被声明为virtual的,那么对于其派生类B而言,fun总是能够被重载的(除非被重写了)。

有的时候我们并不想fun在B类型派生类中被重载,那么怎么办?C++98没有方法对此进行限制,但是c++11就对此作了限定,

那就是使用新的特性 final 说明符来约束,看下面代码:


C++11标准提供了说明符final,用于阻止派生类覆盖特定的虚方法或是阻止一个类成为基类:


//对类进行限定,不让其成为一个类的基类
1. class SomeClass final
{
   //.....
}

//对虚函数进行限定,不让其成子类重载去更改他的特性,因为有时候我们需要保留这样的特性

//又有人说,如果不想改变,那么直接把函数声明为非虚函数不就行了吗?

//其实 final 通常只在继承关系的“中途”终止某个派生类的重载。

//例如 A 派生 B 与 C

//B 在派生 D

//C 在派生 E

//假如在B这条分支上,想控制C使用B的某个方法(并且此方法肯定是从基类A重载来),

//那么可以在B中的这个虚函数后面加上final,这样C就不能在重载了,老老实实使用B的方法

//final只需要在声明时使用final进行修饰就可以了。实现时不需要带上,类似内联inline

2. class SomeClass
{
   type somefun(arg-list) final;
   //.....
}


代码:

#include<iostream>class X{private:char c;public:X():c('B'){}X(char arg):c(arg){}virtual void put()const final{std::cout << c << std::endl;}//final修饰了put()函数,使其不能被覆盖//...}; class Y : public X{private:int i;public:Y():i(0){}Y(int arg):i(arg){}//这里不能有void put();了,因为有了final说明符//...}; class Z final//final修饰Z类,您不能从Z类派生出一个类,而只能用于创建对象{private:double d;public:Z():d(0.0){}Z(double arg):d(arg){}void put()const{std::cout << d << std::endl;}//...}; int main(){char final='X';//可以这么做,因为final不是关键字,只是说明符,最好不要这样用。X x(final);x.put();//输出XY y;y.put();//输出BZ z(3.14159);z.put();//输出3.14159return 0;}

override说明符

C++11中为了帮助程序员写继承结构复杂的类型,引入了虚函数描述符override,如果派生类在虚函数声明时使用了override描述符,那么该函数必须是重载的其基类中的同名函数,否则代码将无法通过编译,这样能避免不小心覆盖基类的虚函数。

class B3{public:    virtual void f() {}};class D3 : public B3{public:    void f() {}};

开发 D3 的程序员真的想重写B3::f函数吗?还是说,他只是不小心写了个与父类同名的函数,却在不经意间导致了覆盖?

为了避免这种错误,C++ 11 引入了override关键字(多么像 C# 啊!)。于是,我们会发现,下面的一段代码是会出错的:

class B4{public:    virtual void g(int) {}};class D4 : public B4{public:    virtual void g(int) override {} // OK    virtual void g(double) override {} // Error};

多亏了override关键字,我们可以让编译器帮我们检测到这个很难发现的程序错误。这段代码的错误在于,override关键字表明,g(double)虽然想要进行override的操作,但实际父类并没有这么个函数。

值得注意的是,这些并不是一些语法糖,而是能确确实实地避免很多程序错误,并且暗示编译器可以作出一些优化。

调用标记了final的virtual函数,例如上面的B2::f,GNU C++ 前端会识别出,这个函数不能被覆盖,

因此会将其从类的虚表中删除。而标记为final的类,例如上面的 B1,编译器则根本不会生成虚表。这样的代码显然更有效率。


0 0