~~~~我的生活,我的点点滴滴!!
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();//输出X
Y y;
y.put();//输出B
Z z(3.14159);
z.put();//输出3.14159
return 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,编译器则根本不会生成虚表。这样的代码显然更有效率。