今天我们要介绍的是其中的一个特性:显式的使用 final
和override
关键字。先来看下面的例子:
1
2
3
|
struct
B1
final
{
}
;
struct
D1
:
B1
{
}
;
// 错误!不能从 final 类继承!
|
上面的代码是错误的,因为 D1 试图继承 B1,而 B1 则声明为 final。很像 Java,不是吗?当然!还有另外的用法:
1
2
3
4
5
6
7
8
9
|
struct
B2
{
virtual
void
f
(
)
final
{
}
// final 函数
}
;
struct
D2
:
B2
{
virtual
void
f
(
)
{
}
}
;
|
这段代码又会出错,因为D2::f
重写了B2::f
,但是B2::f
却被声明为 final 的!
下面再看另外一段代码:
1
2
3
4
5
6
7
8
9
|
struct
B3
{
virtual
void
f
(
)
{
}
}
;
struct
D3
:
B3
{
void
f
(
)
{
}
}
;
|
开发 D3 的程序员真的想重写B3::f
函数吗?还是说,他只是不小心写了个与父类同名的函数,却在不经意间导致了覆盖?为了避免这种错误,C++ 11 引入了override
关键字(多么像 C# 啊!)。于是,我们会发现,下面的一段代码是会出错的:
1
2
3
4
5
6
7
8
9
10
|
struct
B4
{
virtual
void
g
(
int
)
{
}
}
;
struct
D4
:
B4
{
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,编译器则根本不会生成虚表。这样的代码显然更有效率。