使用非虚拟接口习语,可以/将我的非虚拟函数内联

Using the non-virtual-interface idiom, can/will my non-virtual function be inlined ?

本文关键字:虚拟 我的 函数 接口 习语 可以      更新时间:2023-10-16

我最近决定使用非虚拟接口习惯用法(NVI)在c++中设计一个接口,主要是为了使用具有默认值的参数(从而避免由默认参数静态绑定引起的问题)。

我为我的类准备了一个相当简单的声明,看起来像这样:

class Interface{
public:
    void func(Parameter p = 0);
    virtual ~Interface();
private:
    virtual void doFunc(Parameter p)=0;
};
void Interface::func(Parameter p){ doFunc(p); }
Interface::~Interface() {}

我知道在头文件中提供函数体会自动将函数标记为内联候选函数(尽管我不知道将定义放在类之外是否会阻止这种情况)。我也知道虚函数没有内联的原因很明显(我们不知道哪个函数会在运行时被调用,所以我们不能明显地用函数体代替调用)。

那么,在这种情况下,func()是否被标记为内联候选?不是虚函数,但仍然调用虚函数。它使它可以内联吗?

附加问题:值得吗主体只包含一条语句。

请注意,这个问题是为了学习它,而不是到处搜索优化。我知道这个函数只会被调用几次(好吧,就目前而言,因此最好谨慎地考虑程序的演变),并且内联将是相当多余的,并且不是我的程序性能的主要关注点。

谢谢!

我知道在头文件中提供函数体会自动将函数标记为内联的候选函数

More or less;但是你可以通过在类定义中提供函数体来做到这一点,或者通过显式地声明它inline,如果它在头文件中。否则,该函数将受到"一个定义规则"的约束,如果在多个翻译单元中包含标头,则会出现错误。

注意,这并不强制编译器内联所有对函数的调用;在头文件中提供定义只是允许它将其内联到任何包含头文件的翻译单元中,如果它认为值得这样做的话。此外,一些编译器可以执行"整个程序优化",甚至在调用处没有定义时也可以执行内联函数。

那么,在这种情况下,func()会被标记为内联候选吗?它不是虚函数,但仍然调用虚函数。

是的,所有函数都可以内联。如果它们调用自己,显然你不能内联所有的调用;如果函数在编译时不知道(例如,因为它必须虚拟调用,或通过函数指针)。在这种情况下,内联函数将用对doFunc()的虚拟调用取代对func()的直接调用。

请注意,如果动态类型在编译时已知,有时可以内联虚拟调用。例如:

struct MyImplementation : Interface {/*whatever*/};
MyImplementation thing;
thing.func(); // Known to be MyImplementation, func and doFunc can be inlined

附加问题:值得吗?

这取决于"它"是什么。如果您指的是编译时间,那么只要函数保持较短,您可能会获得一些好处(如果函数被多次调用,则可能是显著的),而成本可以忽略不计。如果你指的是花时间选择放置地点的成本,那么可能不是;把它放在最方便的地方。