请考虑以下事项:
在 X.h中:
class X
{
X();
virtual ~X();
};
X.cpp:
#include "X.h"
X::X()
{}
尝试构建这个(我使用的是.dll目标,以避免在丢失的main上出现错误,我正在使用Visual Studio 2010):
错误1错误LNK2001:未解析的外部符号“private:virtual __thiscall X ::〜X(void)”(?? 1X @@ EAE @ XZ)
小修改会导致成功构建,但是:
X.h:
class X
{
inline X(); // Now inlined, and everything builds
virtual ~X();
};
或
X.h:
class X
{
X();
~X(); // No longer virtual, and everything builds
};
当.dtor是虚拟的或者.ctor没有内联时,导致链接器中未解析的外部的原因是什么?
修改
或者,或许更有趣的是,如果我将析构函数设置为非虚拟,或者如果我内联构造函数,为什么不会得到一个未解析的外部?
答案 0 :(得分:21)
你有构造函数的代码 所以它将构造函数构建到目标文件中。构造函数需要将析构函数的地址放入虚拟表中,因为无法找到它,无法构建构造函数。
编译器决定它不需要构建构造函数(因为它将被内联) 因此它不会生成任何代码,因此不需要析构函数的地址。
如果您实例化X类型的对象,它将再次抱怨。
您不需要析构函数的地址来构建构造函数 所以它没有抱怨。
如果你实例化一个X类型的对象,它会抱怨。
答案 1 :(得分:5)
在C ++中,函数必须定义当且仅当它们在程序中使用时(参见3.2 / 2中的ODR)。通常,如果从可能评估的表达式中调用非虚函数,则 。任何非纯虚函数都被视为无条件使用。当[非虚拟]特殊成员函数使用时在语言标准的专用位置定义。等等。
在第一个示例中,您将析构函数声明为非纯虚函数。这立即意味着您的析构函数在您的程序中使用。反过来,这意味着需要该析构函数的定义。您未能提供定义,因此编译器报告了错误。
在第三个示例中,析构函数是非虚拟的。由于您不是在程序中使用析构函数,因此无需定义并编译代码(有关构造析构函数的 use 的详细说明,请参阅12.4)。 / p>
在第二个示例中,您正在处理实现的怪癖,这是由构造函数内联的事实引发的。由于析构函数是非纯虚拟的,因此需要定义。但是,您的编译器无法检测到错误,这就是代码似乎成功编译的原因。您可以在实现的细节中挖掘出这种行为的原因,但从C ++的角度来看,这个例子和第一个例子一样,完全出于同样的原因。
答案 2 :(得分:4)
您需要为虚拟析构函数提供一个正文:
class X
{
X();
virtual ~X() {}
};
答案 3 :(得分:2)
第一个问题的答案,
导致未解决的外部因素的原因 .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
的代码可能位于完全独立的翻译单元中。因此,当编译器进入这个多态类时,即使它不知道你实例化它也不能丢弃它。
答案 4 :(得分:1)
这些还不是一个完整的程序(甚至是一个完整的DLL)。当你收到错误时,你实际上得到了帮助,因为在没有~X()的定义的情况下X无法使用
这意味着在某些情况下,这个特定的编译器实例需要一个定义。即使它编译,它也没有做任何事情。
答案 5 :(得分:1)
我怀疑这是实施定义的行为。这就是为什么
$ 10.3 / 8-“声明了一个虚函数 在一个类中应定义,或 在该类中声明为纯(10.4),或 都;但不需要诊断 (3.2)。“
GCC给出了如下错误,这对于实现虚拟功能的非标准实现细节再次提示(至少对我而言)
/home/OyXDcE/ccS7g3Vl.o:在功能上 X
X::X()': prog.cpp:(.text+0x6): undefined reference to
vtable /home/OyXDcE/ccS7g3Vl.o:在功能上 XX::X()': prog.cpp:(.text+0x16): undefined reference to
vtable collect2:ld返回1退出状态
如果编译器确实需要对OP代码进行诊断,我很困惑,所以考虑发布这个,即使我冒险downvotes :)。当然,我应该猜一个好的编译器。
答案 6 :(得分:0)
你可能正在逃避这个因为constr和destr都是私有的 - 如果在你的构建中没有其他引用类X,那么编译器可能会推断出不需要destr,所以缺少定义就是no不算什么。
这并没有向我解释为什么案例1失败而2和3构建正常。不知道如果两者都公开会发生什么?