Clang-Wweak vtables和纯抽象类

Clang -Wweak-vtables and pure abstract class

本文关键字:抽象类 vtables Clang-Wweak      更新时间:2023-10-16

关于之前关于此主题的问题:

这是我最近问的问题的后续:clang:没有越界的虚拟方法定义(纯抽象C++类)其中被标记为这个问题的重复:clang的含义是什么;s-弱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();
};

测试2.cc

#include "test2.h"
A::A() {;}
A::~A() {;}

我的问题

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

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

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

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

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

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

当创建一个抽象基类时,我的偏好是提供一个越界的虚拟析构函数;即在.cpp文件中实现A::~A()。用户声明的虚拟析构函数的缺点是它隐式地删除了自动生成的复制和移动构造函数&运算符,因此最终需要重新声明它们。根据五条规则,这会产生这样的基类:

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()
{}

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

选项2:禁用警告

如果您愿意,您可以使用Clang的诊断杂注禁用该块的警告:

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

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