Clang -Wweak-vtables和纯抽象类

时间:2015-02-28 23:47:37

标签: c++ clang abstract-class clang++ llvm-clang

关于此主题的先前问题:

这是我最近提出的问题的后续跟进: clang: no out-of-line virtual method definitions (pure abstract C++ class) 并被标记为此问题的副本:What is the meaning of clang's -Wweak-vtables?。我不认为那回答了我的问题,所以我在这里专注于困扰我的事情,但还没有得到回答。

我的情景:

我尝试使用Clang-3.5编译以下简单的C ++代码:

test.h:

class A
{
  public:
    A();
    virtual ~A() = 0;
};

test.cc

#include "test.h"

A::A() {;}
A::~A() {;}

我用来编译它的命令(Linux,uname -r:3.16.0-4-amd64):

$clang-3.5 -Wweak-vtables -std=c++11 -c test.cc

我得到的错误:

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

当A类不是纯抽象时,上面的代码构建得很好。以下代码不会发出警告,唯一的变化是A类不再是抽象的:

test2.h:

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

test2.cc

#include "test2.h"

A::A() {;}
A::~A() {;}

我的问题

纯抽象类有什么特别之处,以上代码会在Clang中触发警告?

1 个答案:

答案 0 :(得分:6)

具有虚方法的类总是需要发出vtable。编译器需要指示存储vtable的位置 - 通常在实现其第一个函数的对象中。

纯抽象类的特别之处是什么?由于它们没有方法,编译器必须在每个转换单元中输出vtable,因此每个转换单元可以引用纯抽象基类型。那是警告告诉你的。

例如,您可能会关心,如果您想避免在非常低的内存环境中复制该内存,或者您正在查看对象并想知道为什么该地方周围有多个vtable副本。

在任何情况下,您可以将多态指针指向A对象,这意味着编译器必须发出有关该类型的一些信息 - vtable。

选项1:实现虚拟方法,例如析构函数

创建抽象基类时我的偏好是提供一个外联的虚拟析构函数;即。在.cpp文件中实现A::~A()。用户声明的虚拟析构函数的缺点是它隐式删除自动生成的复制和移动构造函数&运营商,所以你最终需要重新声明它们。根据{{​​3}},这会产生如下基类:

A.H:

class A {
public:
    A() = default;

    A(const A&) = default;
    A(A&&) = default;
    A& operator=(const A&) = default;
    A& operator=(A&&) = default;
    virtual ~A();

    virtual void doSomething() = 0;
};

A.cpp:

A::~A()
{}

它在技术上不再是纯粹的抽象基类,但它在功能上是相同的。您可以通过基指针获得安全销毁,它仍然允许继承的类被复制和移动构造,并且您可以避免二进制文件中的重复vtable。

选项2:禁用警告

如果您愿意,可以使用rule of five禁用该块的警告:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wweak-vtables"
class A {
public:
    virtual void doSomething() = 0;
    virtual ~A() = 0;
};
#pragma clang diagnostic pop

这就是你的困境:要么让这个课程非纯粹抽象,要么关掉警告。根据您的要求,您可能更喜欢其中一种,但与所有警告一​​样,您应该仔细考虑它。