clang的-Wweak-vtables是什么意思?

我基本上不明白铿锵的-Wweak-vtables 。 这是我迄今观察到的:

情况一:(触发警告)

 class A { public: virtual ~A(){} }; class B : public A { public: virtual ~B(){} }; int main(){} 

情况二:(不触发警告)

 class A { public: virtual ~A(){} }; int main(){} 

案例三:(不触发警告)

 class A { public: virtual ~A(); }; A::~A(){} class B : public A { public: virtual ~B(){} }; int main(){} 

情况四:(触发警告)

 class A { public: virtual ~A(){} virtual void fun(){} }; class B : public A { public: virtual ~B(){} }; int main(){} 

情况五:(不触发警告)

 class A { public: virtual ~A(){} virtual void fun(); }; class B : public A { public: virtual ~B(){} }; int main(){} 

案例六:(不触发警告)

 class A { public: virtual ~A(){} virtual void fun(){} }; class B : public A {}; int main(){} 

案例七:(不触发警告)

 class A { public: virtual ~A(){} virtual void fun(){} }; class B : public A { public: virtual void fun(){} }; int main(){} 

确切的警告是

 warning: 'A' has no out-of-line virtual method definitions; its vtable will be emitted in every translation unit [-Wweak-vtables] 

显然,如果我没有在一个类中声明一个非内联的虚函数,当且仅当我从它派生出来并且派生类有一个虚析构函数的时候,它才会导致某种问题。

问题:

  1. 为什么这是个问题?
  2. 为什么通过声明一个虚函数来解决这个问题呢? (警告提到定义)
  3. 为什么当我不从课堂派生时,警告不会发生?
  4. 为什么派生类没有虚拟析构函数时不会发生警告?

如果所有类的virtual方法都是内联的,那么编译器就没有办法select放置vtable的一个共享副本的翻译单元 – 相反,必须将vtable的副本放置在每个需要它的对象文件中。 在许多平台上,链接器能够统一这些多个副本,或者通过丢弃重复的定义或者将所有引用映射到一个副本,所以这只是一个警告。

在线外实现virtual函数使编译器能够select实现该外联方法的翻译单元作为该类的实现细节的“宿主”,并将该vtable的单个共享副本放置在相同的翻译中单元。 如果多个方法是非线性的,编译器可以任意select方法,只要这个select只由类的声明决定; 例如,GCC按声明顺序select第一个非内联方法。

如果你不重写类的任何方法, virtual关键字没有明显的效果,所以编译器不需要为该类发出一个vtable。 如果你不是从A派生A ,或者如果你没有声明一个派生类的析构函数为virtual ,那么在A中没有重写的方法,因此A的vtable被省略。 如果你声明了一个额外的线外virtual方法来抑制警告,并且做了一些重写A的方法的事情,那么非内联virtual (及其附带的vtable副本)的实现需要在链接的翻译单元,否则链接将失败,因为虚拟表丢失。