标记为“virtual final”的基本 class 方法是否会引入额外的开销?
Does a base class method marked as `virtual final` introduce additional overhead?
假设我想要一个 class Base
有两种方法:foo(int)
和 bar(int)
。我希望它们的定义方式如下:
Base::foo
必须在派生的非抽象 class 中被覆盖
Base::bar
不能在派生的 class 中被覆盖
第一个objective可以通过将foo
标记为virtual int foo(int) = 0
来完成,使其抽象化。第二个要求可以通过将 bar
标记为 virtual int bar(int) final
使其成为最终要求来满足。这是结果代码:
class Base
{
public:
virtual int foo(int n) = 0;
virtual int bar(int n) final
{
return n + 42;
}
};
还有一个例子 class 来自 Base
:
class Derived : public Base
{
public:
virtual int foo(int n) override
{
return n * n;
}
int bar(int n) // compilation error here
{
return n + 43;
}
};
尝试覆盖 Base::bar
已如我们所愿触发编译错误。
现在,我的问题是:将方法标记为 virtual final
是否会引入开销,因为函数是 virtual
(动态调度),即使该函数无论如何都不能被覆盖?
编辑
不要介意缺少虚拟析构函数~Base()
它不是为了让代码更短。
在你的情况下没有开销,因为你的 class 没有继承任何 class 定义你的最终函数,因此无论虚拟声明如何,编译器都会直接调用它的地址。
这是来自实际代码:
b->FoA(); //virtual inherited
002829A7 mov eax,dword ptr [b]
002829AA mov edx,dword ptr [eax]
002829AC mov esi,esp
002829AE mov ecx,dword ptr [b]
002829B1 mov eax,dword ptr [edx]
002829B3 call eax
002829B5 cmp esi,esp
002829B7 call __RTC_CheckEsp (02812E9h)
b->FoB(); // final
002829BC mov ecx,dword ptr [b]
002829BF call A::FoB (0281366h)
编译器可能会将此调用去虚拟化:
struct Base {
virtual int bar(int n) final {
return n + 42;
}
};
struct Derived : Base { };
int foo(Derived& d, int n) {
return d.bar(n);
}
becomes 与 -O1
:
foo(Derived&, int):
lea eax, [rsi+42]
ret
而没有 final
,我们得到一个间接调用:
foo(Derived&, int):
sub rsp, 8
mov rax, QWORD PTR [rdi]
call [QWORD PTR [rax]]
add rsp, 8
ret
假设我想要一个 class Base
有两种方法:foo(int)
和 bar(int)
。我希望它们的定义方式如下:
Base::foo
必须在派生的非抽象 class 中被覆盖
Base::bar
不能在派生的 class 中被覆盖
第一个objective可以通过将foo
标记为virtual int foo(int) = 0
来完成,使其抽象化。第二个要求可以通过将 bar
标记为 virtual int bar(int) final
使其成为最终要求来满足。这是结果代码:
class Base
{
public:
virtual int foo(int n) = 0;
virtual int bar(int n) final
{
return n + 42;
}
};
还有一个例子 class 来自 Base
:
class Derived : public Base
{
public:
virtual int foo(int n) override
{
return n * n;
}
int bar(int n) // compilation error here
{
return n + 43;
}
};
尝试覆盖 Base::bar
已如我们所愿触发编译错误。
现在,我的问题是:将方法标记为 virtual final
是否会引入开销,因为函数是 virtual
(动态调度),即使该函数无论如何都不能被覆盖?
编辑
不要介意缺少虚拟析构函数~Base()
它不是为了让代码更短。
在你的情况下没有开销,因为你的 class 没有继承任何 class 定义你的最终函数,因此无论虚拟声明如何,编译器都会直接调用它的地址。
这是来自实际代码:
b->FoA(); //virtual inherited
002829A7 mov eax,dword ptr [b]
002829AA mov edx,dword ptr [eax]
002829AC mov esi,esp
002829AE mov ecx,dword ptr [b]
002829B1 mov eax,dword ptr [edx]
002829B3 call eax
002829B5 cmp esi,esp
002829B7 call __RTC_CheckEsp (02812E9h)
b->FoB(); // final
002829BC mov ecx,dword ptr [b]
002829BF call A::FoB (0281366h)
编译器可能会将此调用去虚拟化:
struct Base {
virtual int bar(int n) final {
return n + 42;
}
};
struct Derived : Base { };
int foo(Derived& d, int n) {
return d.bar(n);
}
becomes 与 -O1
:
foo(Derived&, int):
lea eax, [rsi+42]
ret
而没有 final
,我们得到一个间接调用:
foo(Derived&, int):
sub rsp, 8
mov rax, QWORD PTR [rdi]
call [QWORD PTR [rax]]
add rsp, 8
ret