【问题标题】:What is the meaning of clang's -Wweak-vtables?clang的-Wweak-vtables是什么意思?
【发布时间】:2014-07-07 22:56:47
【问题描述】:

我基本看不懂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(){}

案例七:(不触发警告)

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. 为什么派生类没有虚拟析构函数时不会出现警告?

【问题讨论】:

    标签: c++ clang


    【解决方案1】:

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

    通过离线实现virtual 函数,编译器可以选择实现该离线方法的翻译单元作为类实现细节的“家”,并放置vtable 在同一个翻译单元中。如果多个方法是脱机的,编译器可以任意选择方法,只要该选择仅由类的声明决定;例如,GCC 选择声明顺序中的第一个非内联方法。

    如果您不覆盖类的任何方法,virtual 关键字没有可观察到的效果,因此编译器无需为该类发出 vtable。如果您不是从A 派生,或者如果您未能声明派生类的析构函数virtual,则A 中没有被覆盖的方法,因此A 的vtable 被省略。如果您声明了一个额外的外联 virtual 方法来抑制警告并执行覆盖 A 中的方法的操作,则非内联 virtual 的实现(及其随附的 vtable 副本)需要在链接的翻译单元中提供,否则链接将失败,因为 vtable 丢失。

    【讨论】:

    • 当同一个类的(不同的)外联方法出现在几个不同的TU中时会发生什么?
    • @Matt,根据类的声明以某种任意但确定性的方式选择了一个外线方法,并且 vtable 最终与方法实现在同一个 TU 中。所有相关的 TU 都看到相同的声明:禁止不一致的声明恶作剧,他们将就所选方法达成一致,从而共同发出一个 vtable。
    • 这个答案没有解释案例 6 和 7。
    • @JeffreyHantin “B 的析构函数不是虚拟的” 是什么意思? B 的编译器生成(隐式声明)析构函数应该是虚拟的(因为基类的析构函数已声明为 virtual)。
    • @Leon,引用规范:“默认且未定义为已删除的析构函数在使用 odr(3.2)或在第一次声明后显式默认时隐式定义。”在案例 6 或 7 中没有使用或显式默认 ~B(),因此编译器不会隐式实例化默认定义。如果它不存在,它怎么可能是虚拟的?这些例子的琐碎性导致了一些违反直觉的效果。
    猜你喜欢
    • 2015-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-12
    • 2017-06-11
    相关资源
    最近更新 更多