考虑一个程序,其中包含一个类Foo
,其中包含一个如此声明的函数Foo::fn
:
virtual void fn();
和Foo
的子类Bar
。将声明Bar::fn
这样:
virtual void fn() override final;
导致fn
中的Bar
或Bar
的子类调用更高效,或者只是保留Bar
的子类来覆盖fn
?如果使用final
提高调用效率,那么定义Bar::fn
的最简单,最有效的方法是什么,以使其功能正好是Foo::fn
?
答案 0 :(得分:4)
如果在fn
中将final
定义为Bar
,则编译器可以通过指针或fn
静态调度Bar
来调用Bar::fn
,因为它知道struct Foo {
virtual void fn();
};
struct Bar : Foo {
void fn() final override;
};
void with_foo(Foo& o) { o.fn(); }
void with_bar(Bar& o) { o.fn(); }
是最终的覆盖者。例如,这个程序片段:
with_foo(Foo&):
subq $8, %rsp
movq (%rdi), %rax
call *(%rax)
addq $8, %rsp
ret
with_bar(Bar&):
subq $8, %rsp
call Bar::fn()
addq $8, %rsp
ret
编译为(See gcc.godbolt.org for details):
with_foo
通过vtable动态调度call *(%rax)
中的呼叫(with_bar
是间接呼叫),但Bar::fn()
中的呼叫静态调度到Bar::fn
。
使Foo::fn
成为Foo::fn
的最终覆盖而不改变行为的最简单方法是将其定义为静态调用struct Bar : Foo {
void fn() final override { Foo::fn(); }
};
:
{{1}}
答案 1 :(得分:3)
我从未关心vtable的大小。它通常相对较小,每类声明只有一个。更麻烦的是在类实例中占用了额外的空间,因为除了单例之外,类实例通常是很多。因此,以某种方式将额外的元素添加到类中肯定会影响内存量。如果真的困扰你的是vtable太大了,那么就做一些重新设计,这样就没有那么多不同的虚拟成员函数(可能将类层次结构分成几个类)或更少的派生类。但实际上,即使你有数百个类,每个类都有一百个虚拟成员函数,它仍然相对较小 - 200个类,100个成员,每个条目需要20000 * 8个字节[64位架构] - &gt ; 160KB。当然20000函数[是的,理论上,你只需要一个新的函数每个派生类需要一个新的vtable,但这是一个愚蠢的设计,实际上不太可能]
final
关键字的目的是确保您不会进一步从中获取 - 例如,如果您有一个基本的类层次结构,其中某些特定的函数不应该是&#34,那么这很有用。 ;变更&#34 ;.比如你说:
class user_base
{
public:
virtual bool check_password(); {... magical code .. };
virtual bool is_super_user() = 0;
};
class superuser : public user_base
{
public:
virtual bool check_password() final
{ .... magical code ...
... extra checks to ensure no remote login...
}
virtual bool is_super_user() final { return true; }
};
class user : public user_base
{
public:
virtual bool is_super_user() final { return false; }
};
你必须耍一下才能确保user_base
不被用作fake_super_user
的基类,当然,还有其他一些大的安全问题。设计,但它给你一些想法。