c – 出乎意料地能够从基类ctor调用派生类虚函数

c – 出乎意料地能够从基类ctor调用派生类虚函数,第1张

概述谁能帮助解释这种意想不到的行为? 前提 我创建了包含成员std :: thread变量的类Thread. Thread的ctor构造成员std :: thread,提供一个指向静态函数的指针,该函数调用纯虚函数(由基类实现). 代码 #include <iostream>#include <thread>#include <chrono>namespace{class Thread 谁能帮助解释这种意想不到的行为?

前提

我创建了包含成员std :: thread变量的类Thread. Thread的ctor构造成员std :: thread,提供一个指向静态函数的指针,该函数调用纯虚函数(由基类实现).

代码

#include <iostream>#include <thread>#include <chrono>namespace{class Thread{public:    Thread()        : mThread(ThreadStart,this)    {        std::cout << __PRETTY_FUNCTION__ << std::endl; // This line commented later in the question.    }    virtual ~Thread() { }    static voID ThreadStart(voID* pObj)    {        ((Thread*)pObj)->Run();    }    voID join()    {        mThread.join();    }    virtual voID Run() = 0;protected:    std::thread mThread;};class Verbose{public:    Verbose(int i) { std::cout << __PRETTY_FUNCTION__ << ": " << i << std::endl; }    ~Verbose() { }};class A : public Thread{public:    A(int i)        : Thread(),mV(i)    { }    virtual ~A() { }    virtual voID Run()    {        for (unsigned i = 0; i < 5; ++i)        {            std::cout << __PRETTY_FUNCTION__ << ": " << i << std::endl;            std::this_thread::sleep_for(std::chrono::seconds(1));        }    }protected:    Verbose mV;};}int main(int argc,char* argv[]){    A a(42);    a.join();    return 0;}

问题

您可能已经注意到,这里有一个微妙的错误:Thread :: ThreadStart(…)是从Thread ctor上下文调用的,因此调用pure / virtual函数不会调用派生类的实现.运行时错误证实了这一点:

pure virtual method calledterminate called without an active exceptionAborted

但是,如果我在Thread ctor中删除对std :: cout的调用,则会出现意外的运行时行为:

virtual voID {anonymous}::A::Run(){anonymous}::Verbose::Verbose(int): : 042virtual voID {anonymous}::A::Run(): 1virtual voID {anonymous}::A::Run(): 2virtual voID {anonymous}::A::Run(): 3virtual voID {anonymous}::A::Run(): 4

即在Thread ctor中删除对std :: cout的调用似乎具有能够从基类`构造函数上下文调用派生类’pure / virtual函数的效果!这与先前的学习和经验不一致.

在windows 10上的Cygwin x64中构建环境.gcc版本是:

g++ (GCC) 5.4.0copyright (C) 2015 Free Software Foundation,Inc.This is free software; see the source for copying conditions.  There is NOwarranty; not even for MERCHANTABIliTY or fitness FOR A PARTIculaR PURPOSE.

我对这种观察感到困惑,并且对于正在发生的事情充满了好奇心.谁能摆脱光明?

解决方法 由于竞争条件,此程序的行为未定义.

但是,如果你想推理它,试试吧.

对于A的构造,这是发生的事情:

> mThread已初始化. *** 作系统安排它在未来的某个时刻开始.
> std :: cout<< __PRETTY_FUNCTION__<<的std :: ENDL; - 从程序的角度来看,这是一个相当慢的 *** 作.
>构造函数运行 – 初始化其vtable(这不是stanard强制要求,但据我所知,所有实现都会这样做).

如果在mThread计划启动之前发生这种情况,您将获得您观察到的行为.否则,您将获得纯虚拟呼叫.

由于这些 *** 作没有以任何方式排序,因此行为未定义.

你可以注意到你从你的base的构造函数中删除了一个相当慢的 *** 作,因此初始化你的派生 – 以及它的vtable – 要快得多.比如说,在OS实际安排mThread的线程开始之前.话虽如此,这并没有解决问题,只是让它不太可能遇到问题.

如果你稍微修改你的例子,你会注意到删除IO代码使竞争更难找到,但没有修正.

virtual voID Run(){    for (unsigned i = 0; i < 1; ++i)    {        std::cout << __PRETTY_FUNCTION__ << ": " << i << std::endl;//      std::this_thread::sleep_for(std::chrono::seconds(1));    }}

主要:

for(int i = 0; i < 10000; ++i){    A a(42);    a.join();}

demo

总结

以上是内存溢出为你收集整理的c – 出乎意料地能够从基类ctor调用派生类虚函数全部内容,希望文章能够帮你解决c – 出乎意料地能够从基类ctor调用派生类虚函数所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1257164.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-07
下一篇 2022-06-07

发表评论

登录后才能评论

评论列表(0条)

保存