简体   繁体   English

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

[英]Why does calling calling a pure virtual method without body does not result in linker error?

I've come across quite weird scenario today. 我今天遇到了很奇怪的情况。 When directly calling a pure virtual method in Interface constructor, I get a undefined reference error. 在接口构造函数中直接调用纯虚方法时,出现未定义的引用错误。

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

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

int main()
{
    A a;
}

Results in: 结果是:

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

However, wrapping a call to fun() in a different method like this: 但是,将对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;
}

Compiles just fine and (obviously) crashes with pure virtual call error. 编译就很好,并且(显然)由于纯虚拟调用错误而崩溃。 I've tested it on latest GCC 8.2.0 and 9.0.0 and Clang 8.0.0. 我已经在最新的GCC 8.2.0和9.0.0和Clang 8.0.0上进行了测试。 Out of those, only GCC produces a linker error in the first case. 在这些情况中,只有GCC在第一种情况下会产生链接器错误。

Wandbox links for a full working example with the error: Wandbox链接显示了完整的工作示例,并显示以下错误:

EDIT: I'm getting flagged for duplication, but I'm not sure how this question is duplicated. 编辑:我被标记为重复,但我不确定如何重复此问题。 It doesn't have anything to do with dangers of calling pure virtual method (from constructor or whatnot), I'm aware of them. 我知道它们与调用纯虚拟方法(从构造函数或其他方法)的危险没有任何关系。

I was trying to understand why the compiler permits this call in one scenario, and fails to do so in another, which was explained very well by Adam Nevraumont. 我试图理解为什么编译器在一种情况下允许此调用,而在另一种情况下却不允许这样做,Adam Nevraumont对此进行了很好的解释。

EDIT2: It seems, that even if callFun is not virtual, it still somehow prevents GCC from devirtualizing and inlining fun call. EDIT2:看来,即使callFun不是虚拟的,它仍会以某种方式阻止GCC取消虚拟化和内联fun呼叫。 See the example below: 请参阅以下示例:

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;
}

You aren't calling the pure virtual function, you are doing a lookup in the vtable for the current entry in the virtual function table for that function. 您不是在调用纯虚函数,而是在vtable中对该函数的虚函数表中的当前条目进行查找。

As it happens, at that point it is a pure virtual function, so you crash due to UB. 碰巧的是,那时它只是一个纯虚函数,因此您由于UB而崩溃。

In the first case, you are getting a linker error because gcc is devirtualizing the call to fun in the ctor. 在第一种情况下,您会收到链接器错误,因为gcc正在取消ctor中对fun的调用的虚拟化。 A devirtualized call to fun directly calls the pure virtual method. fun的非虚拟化调用直接调用纯虚拟方法。 This is possible because while constructing Interface , the compiler knows the state of the virtual function table (derived class modifications to it do not occur yet). 这是可能的,因为在构造Interface ,编译器会知道虚拟功能表的状态(尚未对其进行派生的类修改)。

In the second case, the compiler can devirtualize the call to callFun from the ctor. 在第二种情况下,编译器可以取消ctor对callFun的调用的callFun But the call to fun from within callFun cannot be devirtualized, as callFun could be called from outside the ctor in another method. 但是从callFun内部进行的对fun的调用callFun虚拟化,因为callFun可以通过ctor之外的其他方法进行调用。 Devirtualizing it would be incorrect in the general case . 在一般情况下 ,将其去虚拟化是不正确

In this specific case, if the compiler devirtualized callFun and then inlined it, it could then devirtualize fun in the inlined copy. 在这种特定情况下,如果编译器先对callFun进行callFun ,然后对其进行内联,则可以对内联副本中的fun进行虚拟化。 But the compiler doesn't do this, so no devirtualization occurs. 但是编译器不会执行此操作,因此不会发生虚拟化。

As an aside, you can implement that pure virtual function and cause every example you provided to both link and run fine. 顺便说一句,您可以实现该纯虚函数,并使您提供的每个示例都链接并正常运行。

void Interface::fun() const {}

anywhere in any .cpp file linked in will make your code link, and be correct regardless. 链接到的任何.cpp文件中的任何位置都将使您的代码链接,并且无论如何都是正确的。 Pure virtual doesn't mean "has no implementation" in C++, it just means "derived class must provide an override, and it is legal for me not to have an implementation". 纯虚拟语言在C ++中并不表示“没有实现”,而只是表示“派生的类必须提供重写,并且对我而言,没有实现是合法的”。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么调用纯虚拟链接器错误而不是编译错误? - Why is calling a pure virtual a linker error rather than a compile error? 为什么在 protobuf 消息上调用方法会抛出一个称为错误的纯虚方法? - Why does calling methods on a protobuf Message throw a pure virtual method called error? 为什么在C ++中调用此虚拟函数会导致基类出现“缺少符号”错误? - Why does calling this virtual function in C++ result in a 'missing symbol' error from the base class? 纯虚拟 function 调用 - pure virtual function calling 链接器看不到基类(纯虚拟)中定义的模板函数 - Linker does not see template function defined in a base (pure virtual) class 为什么通过纯虚基 class 调用方法导致 Seg Fault - Why Calling method through pure-virtual base class causing Seg Fault 为什么为模板实例声明运行时多态性会导致链接器错误? - Why does declaring runtime polymorphism for template instantiations result in linker error? 调用虚拟方法而不指向对象? - calling virtual method without pointing to an object? 为什么链接器在虚拟情况下给出错误但在非虚拟情况下却没有? - Why does the linker give me an error in the virtual case but not in the non-virtual case? 为什么在单独的行上调用函数会改变 C++ 中的结果? - Why does calling a function on seperate lines change the result in c++?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM