Clang -Wweak-vtables和纯抽象类

ban*_*ace 8 c++ abstract-class clang llvm-clang clang++

关于此主题的先前问题:

这是我最近提出的问题的后续跟进: clang:没有外线虚拟方法定义(纯抽象C++类) ,并且被标记为此问题的副本:clang的-Wweak-含义是什么?虚函数表?.我不认为那回答了我的问题,所以在这里我专注于困扰我的事情,但尚未得到回答.

我的情景:

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

test.h:

class A
{
  public:
    A();
    virtual ~A() = 0;
};
Run Code Online (Sandbox Code Playgroud)

test.cc

#include "test.h"

A::A() {;}
A::~A() {;}
Run Code Online (Sandbox Code Playgroud)

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

$clang-3.5 -Wweak-vtables -std=c++11 -c test.cc
Run Code Online (Sandbox Code Playgroud)

而我得到的错误:

./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]
Run Code Online (Sandbox Code Playgroud)

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

test2.h:

class A
{
  public:
    A();
    virtual ~A();
};
Run Code Online (Sandbox Code Playgroud)

test2.cc

#include "test2.h"

A::A() {;}
A::~A() {;}
Run Code Online (Sandbox Code Playgroud)

我的问题

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

Ted*_*val 6

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

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

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

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

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

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

啊:

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;
};
Run Code Online (Sandbox Code Playgroud)

A.cpp:

A::~A()
{}
Run Code Online (Sandbox Code Playgroud)

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

选项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
Run Code Online (Sandbox Code Playgroud)

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

  • @Andrzej抽象类=至少一个纯虚方法.纯抽象类== interface =>所有方法都是纯虚拟的吗? (2认同)