什么是“当当脆脆”的意思?

我基本上不理解 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(){}

案例5: (不触发警告)

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


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


int main(){}

案例6: (不触发警告)

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. 当派生类没有虚析构函数时,为什么不发出警告?
21311 次浏览

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

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

如果不重写类的任何方法,则 virtual关键字没有可观察到的效果,因此编译器不需要为类发出 vtable。如果没有从 A派生,或者没有声明派生类的析构函数 virtual,那么在 A中没有重写的方法,因此省略了 A的 vtable。如果你声明一个额外的超行 virtual方法来抑制警告,并且覆盖了 A中的一个方法,那么非内联 virtual的实现(以及它附带的 vtable 副本)需要在一个链接的翻译单元中提供,否则链接将会失败,因为 vtable 丢失了。