Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/131.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么调用没有主体的纯虚拟方法不会导致链接器错误?_C++_Constructor_Undefined Behavior_Virtual Functions_Pure Virtual - Fatal编程技术网

C++ 为什么调用没有主体的纯虚拟方法不会导致链接器错误?

C++ 为什么调用没有主体的纯虚拟方法不会导致链接器错误?,c++,constructor,undefined-behavior,virtual-functions,pure-virtual,C++,Constructor,Undefined Behavior,Virtual Functions,Pure Virtual,我今天遇到了很奇怪的情况。在接口构造函数中直接调用纯虚方法时,我得到一个未定义的引用错误 class Interface { public: virtual void fun() const = 0; Interface(){ fun(); } }; class A : public Interface { public: void fun() const override {}; }; int main() { A a; } 结果: prog.c

我今天遇到了很奇怪的情况。在接口构造函数中直接调用纯虚方法时,我得到一个未定义的引用错误

class Interface
{
public:    
    virtual void fun() const = 0; 
    Interface(){ fun(); }
};

class A : public Interface
{
public:
    void fun() const override {};
};

int main()
{
    A a;
}
结果:

prog.cc: In constructor 'Interface::Interface()':
prog.cc:5:22: warning: pure virtual 'virtual void Interface::fun() const' called from constructor
5 |     Interface(){ fun(); }
  |                      ^
/tmp/ccWMVIWG.o: In function `main':
prog.cc:(.text.startup+0x13): undefined reference to `Interface::fun() const'
collect2: error: ld returned 1 exit status
但是,使用以下不同的方法包装对fun()的调用:

class Interface
{
public:    
    virtual void fun() const = 0; 
    Interface(){ callfun(); }
    virtual void callfun()
    {
        fun();
    }
};

class A : public Interface
{
public:
    void fun() const override {};
};

int main()
{
    A a;
}
编译得很好,并且(显然)由于纯虚拟调用错误而崩溃。 我已经在最新的GCC 8.2.0和9.0.0以及Clang8.0.0上测试了它。其中,在第一种情况下,只有GCC生成链接器错误

Wandbox链接以获取完整的工作示例,并显示错误:

编辑: 我被标记为重复,但我不确定这个问题是如何重复的。这和调用纯虚拟方法(从构造函数或其他什么)的危险无关,我知道

我试图理解为什么编译器在一个场景中允许这个调用,而在另一个场景中却不允许,Adam Nevraumont对此做了很好的解释

编辑2: 看起来,即使
callFun
不是虚拟的,它仍然以某种方式阻止GCC对
fun
调用进行虚拟化和内联。请参见下面的示例:

class Interface
{
public:    
    virtual void fun() const = 0; 
    Interface(){ callfun(); }
    void callfun()
    {
        fun();
    }
};

class A : public Interface
{
public:
    void fun() const override {};
};

int main()
{
    A a;
}

    • 您不是在调用纯虚拟函数,而是在vtable中查找该函数的虚拟函数表中的当前条目

      碰巧的是,在这一点上,它是一个纯虚拟函数,因此由于UB而崩溃

      在第一种情况下,您将得到一个链接器错误,因为gcc正在对ctor中的
      fun
      调用进行设备化。对
      fun
      的设备化调用直接调用纯虚拟方法。这是可能的,因为在构造
      接口时,编译器知道虚拟函数表的状态(尚未对其进行派生类修改)

      在第二种情况下,编译器可以从ctor对
      callFun
      的调用进行设备化。但是从
      callFun
      内部调用
      fun
      不能被反虚拟化,因为
      callFun
      可以通过另一种方法从外部调用。在一般情况下,将其虚拟化是不正确的

      在这种特定情况下,如果编译器将
      callFun
      设备化,然后将其内联,那么它可以在内联副本中设备化
      fun
      。但是编译器不这样做,因此不会发生设备化

      另一方面,您可以实现这个纯虚拟函数,并使您提供的每个示例都链接并运行良好

      void Interface::fun() const {}
      

      链接到的任何
      .cpp
      文件中的任何位置都将使您的代码链接,并且无论怎样都是正确的。纯虚并不意味着C++中没有实现,它只是意味着“派生类必须提供重写,并且我没有实现它是合法的”。警察很清楚这很危险。OP询问编译器为什么没有发现问题。可能是重复的我不确定这是重复的吗?我完全意识到这一点的危险性,我试图理解为什么编译器可以在一种情况下警告我,而在另一种情况下却没有这样做。这不是重复。别担心,标记为可能的重复只是问题作为重复关闭之前的第一步,即使这样也可以重新打开它。此代码没有主代码,因此它不可能“崩溃”,甚至不应该进入链接阶段。发布真实代码,而不是指向其他地方的链接。啊哈!我曾考虑过编译器试图内联函数,但由于缺少主体而未能内联,但不知道如何实现设备化。我假设只有在启用优化的情况下才会发生这种情况。非常感谢。这可能是允许纯虚拟方法有定义的原因吗?我仍然想知道为什么会是这样…@user463035818如果它对您有任何帮助,我认为纯虚拟方法是一种强制派生类来实现此函数的方法。您可以让派生类调用pure virtual base方法来预先进行一些计算,然后让派生类自己进行更多的计算。@MarcinBąk wow cool。这是有道理的。我从未考虑过从派生类调用基实现的可能性。在这种情况下,我总是使用虚拟的(但不是纯的)method@MarcinBąk是的,因为除非是内联的,否则无法进行设备化。您可以在ctor内对调用进行设备化,因为您知道ctor中vtable的确切状态;在ctor中调用的函数可以从其他地方调用,因此设备化要求首先将它们内联到ctor中(即使这样,优化器也可能不会在内联过程之后执行设备化过程)