C++ 叮当声-Wweak vtables和纯抽象类

C++ 叮当声-Wweak vtables和纯抽象类,c++,clang,abstract-class,clang++,llvm-clang,C++,Clang,Abstract Class,Clang++,Llvm Clang,关于此主题之前的问题: 这是我最近提出的问题的后续: 并被标记为此问题的副本:。我不认为这回答了我的问题,所以在这里我把重点放在让我困惑的事情上,这个问题还没有得到回答 我的场景: 我试图用CLAN-3.5:编译以下简单的C++代码 测试h: class A { public: A(); virtual ~A() = 0; }; test.cc #include "test.h" A::A() {;} A::~A() {;} 我用于编译此文件的命令(Linux,unam

关于此主题之前的问题:

这是我最近提出的问题的后续: 并被标记为此问题的副本:。我不认为这回答了我的问题,所以在这里我把重点放在让我困惑的事情上,这个问题还没有得到回答

我的场景:

我试图用CLAN-3.5:

编译以下简单的C++代码 测试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):

我得到的错误是:

./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不再是抽象的:

测试2.h:

class A
{
  public:
    A();
    virtual ~A();
};
test2.cc

#include "test2.h"

A::A() {;}
A::~A() {;}
我的问题


纯抽象类有什么特别之处,以至于上面的代码会发出叮当声警告?

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

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

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

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

选项1:实现一个虚拟方法,比如析构函数 我在创建抽象基类时的首选方法是提供一个越界的虚拟析构函数;例如,在.cpp文件中实现
A::~A()
。用户声明的虚拟析构函数的缺点是,它隐式删除自动生成的复制和移动构造函数和运算符,因此最终需要重新声明它们。根据,这将产生如下基类:

A.h:

A.cpp:

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

选项2:禁用警告 如果愿意,可以使用以下选项禁用该块的警告:

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

这就是你的困境:要么使类非纯抽象,要么关闭警告。根据您的需求,您可能更喜欢一个或另一个,但是正如所有警告一样,您应该仔细考虑它。

在第二个例子中,代码> < /COD>不是抽象的,如果您没有定义“代码> A::A/<代码>,您认为会发生什么?然后,如果编译器等待查看
a::~a
的定义以发出vtable,那么就不会有vtable了@这是正确的行为,不是吗?(包括test2.h在内的其他TU也会得出相同的结论)因为当纯抽象类没有方法时?@Andrzej abstract class=至少有一个纯虚拟方法。纯抽象类==接口=>所有的方法都是纯虚拟的?@Ted我还是很困惑。在这两个示例中,我都定义了A::~A()。这如何等同于没有方法?为什么删除构造函数也能解决这个问题:
classa{public:virtual~A()=0;}(假设在test.cc中也删除了A::A())>这与没有方法有什么区别?不管你做什么,这些方法仍然存在,因为C++需要它们;如果您没有明确地使用它们,它们将由编译器自动生成。您的示例没有越界的虚拟方法定义。您有一个离线非虚拟方法(
A()
)和一个在线虚拟方法(
~A()
)。像构造函数这样的非虚拟方法是无关紧要的,因为它们不需要vtable。像
~A()
这样的虚拟方法需要vtable,但没有实现。您需要的是一个越界的虚拟方法。我最初回答关于纯抽象类的问题,因为这是问题的标题,但给出的示例确实不是纯抽象的,因为它有一个用户定义的构造函数。
A::~A()
{}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wweak-vtables"
class A {
public:
    virtual void doSomething() = 0;
    virtual ~A() = 0;
};
#pragma clang diagnostic pop