C ++最终是否意味着所有方面的最终结果?

时间:2016-04-27 15:59:26

标签: c++ c++11 virtual final

  

C ++ 11最后添加。

最后!

我理解final会做两件事:

  • 使课程不可继承。
  • 使类中的(虚拟)函数不可覆盖(在派生类中)。

这两者似乎彼此独立。但请举例说明以下内容:

class Foo
{
    public:
    virtual void bar()
    {
        //do something unimportant.
    }
};
class Baz final : public Foo
{
    public:
    void bar() /*final*/ override
    {
        //do something more important than Foo's bar.
    }
};

从上面看,我认为Bazfinal应该 NOT 需要指定virtual成员函数{ {1}}也是bar。由于final无法继承,因此覆盖Baz的问题超出了范围。不过我的编译VC ++ 2015,对此非常安静。我目前还没有对其他任何人进行测试。

如果有人能够对这个话题有所了解,我会很高兴的。标准的引用(如果有的话)将非常感激。另请说明我不知道的任何角落案件,这可能导致我的逻辑信念失败。

所以,我的问题是: bar 隐含暗示其 final class 函数 {{ 1}} 也是?应该是?请澄清。

我之所以这样问是因为virtual功能符合去虚拟化的条件,这是一项很好的优化。任何帮助表示赞赏。

3 个答案:

答案 0 :(得分:11)

  

我之所以这么说是因为最终功能符合去虚拟化的要求,这是一个很好的优化。

他们呢? "去虚拟"不是C ++标准的一部分。或者至少,不是真的。

去虚拟化仅仅是"仿佛"规则,规定实施可以做任何它喜欢的事情,只要实施行为"好像"它正在做标准所说的。

如果编译器可以在编译时检测到对虚拟成员函数的特定调用,通过多态类型,将无可否认地调用该函数的特定版本,则允许它避免使用虚拟调度逻辑和调用静态的功能。这表现为"好像"它使用了虚拟调度逻辑,因为编译器可以证明这是本来会被调用的函数。

因此,该标准没有定义何时允许/禁止去虚拟化。编译器在内联一个带有指向虚拟类型的指针的类时,可能会发现正在传递的指针指向在其内联的函数中声明的本地堆栈变量。或者编译器可以将特定的内联/调用图跟踪到特定多态指针/引用的原点。在这些情况下,编译器可以将调用去虚拟化为该类型。但只有它足够聪明才能这样做。

编译器是否会将所有虚函数调用虚拟化为final类,无论这些方法是否自己声明为final?有可能。它可能不会。它甚至可能不会对在多态类型上声明为final的方法的任何调用进行虚拟化。这是一个有效的(如果不是特别明亮的)实现。

您提出的问题是具体实施。它可能因编译器而异。

但是,正如您所指出的,正在声明final的类应该是编译器将所有对指针/引用的调用虚拟化为final类类型的充分信息。如果编译器没有这样做,那么这是一个实施质量问题,而不是标准问题。

答案 1 :(得分:5)

从这里引用C ++标准草案[class.virtual/4]

  

如果某个类f中的虚拟函数B标有 virt-specifier final,并且在D派生的类BD::f函数B::f覆盖final,该程序格式错误。

这里[class/3]

  

如果某个类标有 class-virt-specifier final,并且它在库中显示为基类型说明符 -clause (条款[class.derived]),该程序格式不正确。

所以,回答问题;

  

class virtual是否隐含地暗示其final函数也是final class?应该是?请澄清。

所以,至少不是正式的。任何违反这两条规则的企图在两种情况下都会产生相同的结果;该计划格式不正确,无法编译。 virtual表示无法派生类,因此无法覆盖其final方法。

应该吗?至少在形式上,可能不是;他们是相关的,但他们不是一回事。也没有必要正式要求一方暗示另一方,效果自然而然。任何违规都有相同的结果,编译失败(希望有适当的错误消息来区分两者)。

触及您的查询动机和虚拟呼叫的解虚拟化。这并不总是立即受到类的<h1>Title</h1>\n<img class="cover" src="someimg.jpg">\n<p>Introduction</p> 或方法的影响(尽管它们提供了帮助),虚拟函数和类层次结构的常规规则也适用。

如果编译器可以确定在运行时将始终调用特定方法(例如,使用自动对象,即#34;在堆栈上#34;),它可以应用此类an optimisation anyway,而不管方法是最终的还是不是。这些优化属于as-if规则,允许编译器apply any transformation,只要可观察行为与原始代码的执行完全相同。

答案 2 :(得分:0)

  

final类是否隐式暗示其virtual函数也将成为final

     

[...]

     

我问这是因为最终功能可以进行去虚拟化了,这是一个很好的优化。

,出于去虚拟化的目的,它在所有主要编译器(包括MSVC)中都这样做:

struct B                   { virtual void f() = 0;   };

struct D1       : public B {         void f();       };
struct D2       : public B {         void f() final; };
struct D3 final : public B {         void f();       };

void f1(D1& x) { x.f(); } // Not de-virtualized
void f2(D2& x) { x.f(); } //     De-virtualized
void f3(D3& x) { x.f(); } //     De-virtualized