为什么这个虚拟析构函数会触发一个未解析的外部?

发布于 2024-09-15 12:00:31 字数 861 浏览 4 评论 0原文

考虑以下内容:

Xh:

class X
{
    X();
    virtual ~X();
};

X.cpp:

#include "X.h"

X::X()
{}

中尝试构建这个(我使用 .dll 目标来避免丢失主程序上的错误,并且我'我使用 Visual Studio 2010):

错误 1 ​​错误 LNK2001:无法解析的外部符号“private: virtual __thiscall X::~X(void)”(??1X@@EAE@XZ)

小修改会导致成功构建,但是:

Xh:

class X
{
    inline X(); // Now inlined, and everything builds
    virtual ~X();
};

Xh:

class X
{
    X();
    ~X(); // No longer virtual, and everything builds
};

当 .dtor 是虚拟的或 .ctor 未内联时,是什么导致链接器中无法解析外部?

编辑:

或者,也许更有趣的是,如果我将析构函数设置为非虚拟的,或者如果我内联构造函数,为什么我不会得到未解析的外部?

Consider the following:

In X.h:

class X
{
    X();
    virtual ~X();
};

X.cpp:

#include "X.h"

X::X()
{}

Try to build this (I'm using a .dll target to avoid an error on the missing main, and I'm using Visual Studio 2010):

Error 1 error LNK2001: unresolved external symbol "private: virtual __thiscall X::~X(void)" (??1X@@EAE@XZ)

Small modifications result in a successful build, however:

X.h:

class X
{
    inline X(); // Now inlined, and everything builds
    virtual ~X();
};

or

X.h:

class X
{
    X();
    ~X(); // No longer virtual, and everything builds
};

What causes the unresolved external in the linker when the .dtor is virtual or when the .ctor isn't inlined?

EDIT:

Or, perhaps more interestingly, why do I not get an unresolved external if I make the destructor non-virtual, or if I inline the constructor?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

念三年u 2024-09-22 12:00:31

情况 1:

您拥有构造函数的代码。
因此它将构造函数构建到目标文件中。构造函数需要将析构函数的地址放入虚拟表中,因为找不到它,构造函数无法构建。

情况 2:(内联构造函数)

编译器决定不需要构建构造函数(因为它将被内联)。
因此,它不会植入任何代码,因此不需要析构函数的地址。

如果你实例化一个 X 类型的对象,它会再次抱怨。

情况3:(非虚析构函数)

构建构造函数不需要析构函数的地址。
所以它不抱怨。

如果你实例化一个 X 类型的对象,它会抱怨。

Situation 1:

You have the code for the constructor.
So it builds the constructor into the object file. The constructor needs the address of the destructor to put into the virtual table because it can not find it the constructor can not be built.

Situation 2: (inline constructor)

The compiler decides it does not need to build the constructor (as it will be inlined).
As such it does not plant any code and therefore does not need the address of the destructor.

If you instanciate an object of type X it will again complain.

Situation 3: (non virtual destructor)

You don't need the address of the destructor to build the constructor.
So it does not complain.

It will complain if you instantiate an object of type X.

压抑⊿情绪 2024-09-22 12:00:31

您需要为虚拟析构函数提供一个主体:


class X
{
    X();
    virtual ~X() {}
};

You need to give a body to the virtual destructor:


class X
{
    X();
    virtual ~X() {}
};
一个人练习一个人 2024-09-22 12:00:31

在 C++ 中,当且仅当在程序中使用函数时,才必须定义函数(请参阅 3.2/2 中的 ODR)。一般来说,如果从可能计算的表达式中调用非虚函数,就会使用。任何非纯虚函数都被视为无条件使用。当使用[非虚拟]特殊成员函数时,使用是在语言标准的专用位置定义的。等等。

  • 在您的第一个示例中,您将析构函数声明为非纯虚函数。这立即意味着您的程序中使用了您的析构函数。反过来,这意味着需要该析构函数的定义。您未能提供定义,因此编译器报告了错误。

  • 在您的第三个示例中,析构函数是非虚拟的。由于您没有在程序中使用析构函数,因此不需要定义并且代码可以编译(有关析构函数使用的详细描述,请参阅 12.4)。< /p>

  • 在您的第二个示例中,您正在处理实现的一个怪癖,这是由构造函数内联这一事实触发的。由于析构函数是非纯虚拟的,因此需要定义。但是,您的编译器未能检测到该错误,这就是代码似乎编译成功的原因。您可以在实现的细节中挖掘此行为的原因,但从 C++ 的角度来看,此示例与第一个示例一样损坏,原因完全相同。

In C++ functions have to be defined if and only if they are used in your program (see the ODR in 3.2/2). In general, non-virtual functions are used if they are called from potentially evaluated expressions. Any non-pure virtual function is considered unconditionally used. When [non-virtual] special member functions are used is defined in dedicated locations of the language standard. And so on.

  • In your first example, you declared your destructor as a non-pure virtual function. This immediately means that your destructor is used in your program. This, in turn, means that the definition of that destructor is required. You failed to provide a definition, so the compiler reported an error.

  • In your third example the destructor is non-virtual. Since you are not using the destructor in your program, no definition is required and the code compiles (see 12.4 for detailed description of what constitutes a use of a destructor).

  • In your second example you are dealing with a quirk of the implementation, triggered by the fact that the constructor is inlined. Since the destructor is non-pure virtual, the definition is required. However, your compiler failed to detect the error, which is why the code seems to compile successfully. You can dig for the reasons of this behavior in the details of the implementation, but from the C++ point of view this example is as broken as the first one for exactly the same reason.

神魇的王 2024-09-22 12:00:31

你第一个问题的答案,

什么原因导致无法解析的外部输入
.dtor 为虚拟时的链接器
或者当 .ctor 没有内联时?

...很简单,您没有析构函数的定义。

现在你的第二个问题更有趣:

为什么我没有得到未解决的问题
如果我创建析构函数,则为外部
非虚拟,或者如果我内联
构造函数?

原因是因为你的编译器不需要 X 的析构函数,因为你从未实例化 X,所以它抛弃了你的整个类。如果您尝试编译此程序,您将得到一个未解析的外部:

class X
{
public:
    X();
     ~X();
};

X::X() {};

int main()
{
    X x;
    return 0;
}

但如果您注释掉 X x; ,正如您所观察到的那样,它会编译得很好。

现在让我们回过头来看看为什么如果析构函数是virtual,它就无法编译。我在这里推测,但我相信原因是因为,由于您有一个虚拟析构函数,所以 X 现在是一个多态类。为了在内存中布局多态类,使用 vtable 需要每个虚拟函数的地址。您尚未实现 X::~X,因此无法解析外部结果。

为什么编译器不像 X 不是多态类时那样直接丢弃 X 呢?这里有更多猜测。但我认为原因是因为即使您没有直接实例化 X,也无法确定代码中没有 X 处于活动状态,伪装成某种东西别的。例如,考虑一个抽象基类。在这种情况下,您永远不会直接实例化 Base,并且 Derived 的代码可能位于完全独立的翻译单元中。因此,当编译器到达这个多态类时,即使它不知道您实例化了它,它也无法丢弃它。

The answer to your first question,

What causes the unresolved external in
the linker when the .dtor is virtual
or when the .ctor isn't inlined?

...is, quite simply, that you don't have a definition for the destructor.

Now your second question is somewhat more interesting:

why do I not get an unresolved
external if I make the destructor
non-virtual, or if I inline the
constructor?

And the reason is because your compiler didn't need X's destructor since you never instantiated X, so it threw your whole class away. If you try to compile this program, you will get an unresolved external:

class X
{
public:
    X();
     ~X();
};

X::X() {};

int main()
{
    X x;
    return 0;
}

But if you comment out X x; it will compile just fine, as you have observed.

Now let's come back around to why it won't compile if the destructor if virtual. I'm speculating here, but I believe the reason is because, since you have a virtual destructor, X is now a polymorphic class. In order to lay-out polymorphic classes in memory, compilers that implement polymorphism using a vtable need the adresses to every virtual function. You haven't implemented X::~X, so an unresolved external results.

Why doesn't the compiler just throw X away as it did when X was not a polymorphic class? More speculation here. But I expect the reason is because even if you haven't directly instantiated X, it can't be sure that nowhere in your code does an X live, masqerading as something else. For an example, consider an abstract base class. In this case, you'll never instantiate Base directly and the code for Derived might be in a totally seperate translation unit. So when the compiler gets to this polymorphic class, it can't discard it even if it doesn't know you instantiated it.

凡尘雨 2024-09-22 12:00:31

这些还不是一个完整的程序(甚至不是一个完整的 DLL)。当您收到错误时,您实际上得到了帮助,因为如果没有 ~X() 的定义,X 就无法使用。

这意味着这个特定的编译器实例在某些情况下需要它的定义。即使编译通过,它也没有做任何事情。

These aren't a complete program yet (or even a complete DLL). When you are getting the error, you are actually being helped, because X is unusable without a definition for ~X()

All it means is that this specific compiler instance needed a definition for it in some cases. Even if it compiles, it doesn't do anything.

相守太难 2024-09-22 12:00:31

我怀疑这是实现定义的行为。这就是为什么

$10.3/8-“声明了一个虚函数
在一个类中应被定义,或
在该类别中声明纯(10.4),或
两个都;但不需要诊断
(3.2)。”

GCC 给出如下错误,这再次强烈暗示(至少对我来说)关于实现虚拟函数的非标准实现细节

/home/OyXDcE/ccS7g3Vl.o:功能中
X::X()': prog.cpp:(.text+0x6):
X' 的 vtable 未定义引用
/home/OyXDcE/ccS7g3Vl.o:功能中
X::X()': prog.cpp:(.text+0x16):
X' 的 vtable 未定义引用
collect2:ld返回1退出状态


我很困惑OP代码是否真的需要编译器进行诊断,所以考虑发布这个,即使我冒着投反对票的风险:)。当然,我猜应该是一个好的编译器。

I have a suspicion on this one that this is implementation defined behavior. Here's why

$10.3/8- "A virtual function declared
in a class shall be defined, or
declared pure (10.4) in that class, or
both; but no diagnostic is required
(3.2)."

GCC gives error such as below, which again, is highly suggestive (to me at least) about a non-standard implementation detail of implementing virtual functions

/home/OyXDcE/ccS7g3Vl.o: In function
X::X()': prog.cpp:(.text+0x6):
undefined reference to
vtable for X'
/home/OyXDcE/ccS7g3Vl.o: In function
X::X()': prog.cpp:(.text+0x16):
undefined reference to
vtable for X'
collect2: ld returned 1 exit status

I am confused if a diagnostic is really required from a compiler for the OP code, so thought of posting this, even as I risk downvotes :). Of course, a good compiler should I guess.

静若繁花 2024-09-22 12:00:31

您可能会逃脱这一点,因为 constr 和 destr 都是私有的 - 如果您的构建中没有对类 X 的其他引用,那么编译器可能会推断出 destr 不是必需的,因此缺少定义并不是什么大问题。

这并不能向我解释为什么情况 1 失败,而情况 2 和 3 构建正常。想知道如果两者都公开会发生什么吗?

You may be getting away with this because both constr and destr are private - if there is no other ref to class X in your build then the compiler may be deducing that the destr is not required, so lack of a definition is no biggie.

This does not explain to me why case 1 fails while 2 and 3 build OK though. Wonder what happens if both are made public?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文