我基本上不理解clang的-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(){}
案例7:(不触发警告)
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]
显然,如果我没有在类中声明非内联虚函数,它会导致一些 当且仅当我从它派生并且派生类具有虚拟析构函数时才出现问题。
问题:
答案 0 :(得分:89)
如果所有类的virtual
方法都是内联的,则编译器无法选择在其中放置vtable的单个共享副本的转换单元 - 而是vtable的副本必须放在需要它的每个目标文件中。在许多平台上,链接器能够通过丢弃重复的定义或将所有引用映射到一个副本来统一这些多个副本,因此这只是一个警告。
在线外实现virtual
函数使编译器能够选择实现该外联方法的翻译单元作为" home"对于类的实现细节,并将vtable的单个共享副本放在同一个翻译单元中。如果多个方法是脱节的,编译器可以选择任意方法,只要该选择仅由类的声明确定;例如,GCC在声明顺序中选择第一个非内联方法。
如果您不覆盖类的任何方法,virtual
关键字没有可观察到的影响,因此编译器不需要为该类发出vtable。如果您不是从A
派生,或者如果您未能声明派生类的析构函数virtual
,则A
中没有被覆盖的方法,因此A
省略了vtable。如果您声明了一个额外的外行virtual
方法来取消警告并执行覆盖A
中某个方法的操作,则会实现非内联virtual
(及其随附的vtable副本)需要在链接的翻译单元中提供,否则链接将因为缺少vtable而失败。