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]
显然,如果我没有在一个类中声明一个非内联的虚函数,当且仅当我从它派生出来并且派生类有一个虚析构函数的时候,它才会导致某种问题。
问题:
- 为什么这是个问题?
- 为什么通过声明一个虚函数来解决这个问题呢? (警告提到定义)
- 为什么当我不从课堂派生时,警告不会发生?
- 为什么派生类没有虚拟析构函数时不会发生警告?
如果所有类的virtual
方法都是内联的,那么编译器就没有办法select放置vtable的一个共享副本的翻译单元 – 相反,必须将vtable的副本放置在每个需要它的对象文件中。 在许多平台上,链接器能够统一这些多个副本,或者通过丢弃重复的定义或者将所有引用映射到一个副本,所以这只是一个警告。
在线外实现virtual
函数使编译器能够select实现该外联方法的翻译单元作为该类的实现细节的“宿主”,并将该vtable的单个共享副本放置在相同的翻译中单元。 如果多个方法是非线性的,编译器可以任意select方法,只要这个select只由类的声明决定; 例如,GCC按声明顺序select第一个非内联方法。
如果你不重写类的任何方法, virtual
关键字没有明显的效果,所以编译器不需要为该类发出一个vtable。 如果你不是从A
派生A
,或者如果你没有声明一个派生类的析构函数为virtual
,那么在A
中没有重写的方法,因此A
的vtable被省略。 如果你声明了一个额外的线外virtual
方法来抑制警告,并且做了一些重写A
的方法的事情,那么非内联virtual
(及其附带的vtable副本)的实现需要在链接的翻译单元,否则链接将失败,因为虚拟表丢失。