Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.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++_Pure Virtual - Fatal编程技术网

C++ 如何修复称为运行时错误的纯虚拟函数?

C++ 如何修复称为运行时错误的纯虚拟函数?,c++,pure-virtual,C++,Pure Virtual,我理解为什么会出现错误(调用纯虚拟函数)。我试图从下面显示的基类的析构函数中调用纯虚函数。但是,我不知道如何重新编写代码来防止这种情况发生。以下是基类和派生类(无论如何都是相关部分): 基类: TailFileManager::TailFileManager(const std::string &filename, const int fileOpenPeriod_ms) : m_Stop(false) { m_WorkerThread.reset(new boost::thre

我理解为什么会出现错误(调用纯虚拟函数)。我试图从下面显示的基类的析构函数中调用纯虚函数。但是,我不知道如何重新编写代码来防止这种情况发生。以下是基类和派生类(无论如何都是相关部分):

基类:

TailFileManager::TailFileManager(const std::string &filename, const int fileOpenPeriod_ms)
: m_Stop(false)
{
    m_WorkerThread.reset(new boost::thread(boost::bind(&TailFileManager::TailFile, this, filename, fileOpenPeriod_ms)));
}

TailFileManager::~TailFileManager()
{
    m_Stop = true;
    m_WorkerThread->join();
}

void TailFileManager::TailFile(const std::string &filename, const int fileOpenPeriod_ms)
{
    std::ifstream ifs(filename.c_str());

    while (! ifs.is_open())
    {
        boost::this_thread::sleep(boost::posix_time::milliseconds(fileOpenPeriod_ms));
    ifs.open(filename.c_str());
    }

    ifs.seekg(0, std::ios::end);

    while (! m_Stop)
    {
        ifs.clear();

        std::string line;

        while (std::getline(ifs, line))
        {
            OnLineAdded(line);
        }

        OnEndOfFile();
    }

    ifs.close();
}
派生类:

ETSLogTailFileManager::ETSLogTailFileManager(const std::string &filename, const int heartbeatPeriod_ms)
: TailFileManager(filename, heartbeatPeriod_ms),
  m_HeartbeatPeriod_ms(heartbeatPeriod_ms),
  m_FoundInboundMessage(false),
  m_TimeOfLastActivity(0)
{
}

ETSLogTailFileManager::~ETSLogTailFileManager()
{
}

void ETSLogTailFileManager::OnLineAdded(const std::string &line)
{
    // do stuff...
}

void ETSLogTailFileManager::OnEndOfFile()
{
    // do stuff...
}

您不应该在构造或销毁期间调用虚拟函数,因为这些调用不会按照您的想法执行,如果它们执行了,您仍然会不高兴。如果你是一个正在恢复的java或C程序员,请密切注意这个项目,因为这是一个地方,那些语言Zigg,而C++ +ZAG.<

重新设计您的设计,即在对象被破坏之前调用一些清理函数,在CONST/DEST(如果有的话)中,只要避免虚拟函数,如果您正在使用C++……/P> 虚拟调用的规则是不同的。C++ 2003,第12.7节“构造和破坏”,说:

让我们重温一些过去的记忆

构件函数,包括虚拟函数(10.3),可在构造或销毁过程中调用(12.6.2)。当从构造函数(包括数据成员的mem初始值设定项)或析构函数直接或间接调用虚函数时,且调用所应用的对象是正在构造或销毁的对象,调用的函数是在构造函数或析构函数自己的类或其一个基类中定义的函数,但不是在从构造函数或析构函数类派生的类中重写它的函数,也不是在派生最多的对象(1.8)的另一个基类中重写它的函数。如果虚拟函数调用使用显式类成员访问(5.2.5),并且对象表达式引用正在构造或销毁的对象,但其类型既不是构造函数或析构函数自己的类,也不是它的一个基,则调用的结果是未定义的

由于行为上的这种差异,建议在构建或销毁对象时不要调用对象的虚拟函数。

在构造或销毁期间,切勿调用虚拟函数 摘自《有效C++》第三版 作者:斯科特·迈尔斯 2005年6月6日

你写

“我试图从下面显示的基类的析构函数中调用纯虚拟函数。”

问题的代码是

TailFileManager::~TailFileManager()
{
    m_Stop = true;
    m_WorkerThread->join();
}
幸运的是,在单线程执行中,这不可能调用纯虚拟函数。但是您正在加入的线程可能会调用此对象上的纯虚拟函数,可能是通过非虚拟成员函数。如果是这样,那么问题在于线程,特别是这个对象的生命周期管理


很遗憾,您没有显示相关代码。试着把事情简化成一个小的、完整的、有效的例子。在“工作”的意义上,它是复制问题的。

< P>关于C++标准的相关:

  • 如果在构造函数或析构函数中调用虚函数,则该函数为 动态调度,就像其动态类型是当前正在执行的构造函数/析构函数一样(§12.7/4)
  • 如果该功能发生在纯虚拟机上,则这是未定义的行为(§10.4/6);定义行为:调用
    \ucxa\upure\uvirtual
所以,你有一个棘手的问题


解决这个问题的一个可能办法是将其分为两部分,以便将破坏分为两部分。这可以通过
策略
模式实现:

  • 提供一个可定制的界面,你的策略
  • 提供一个manager类,该类封装了功能并遵从可定制部件的策略
让我们更清楚地说:

class Interface {
public:
    friend class Manager;

private:
    virtual void finalize() = 0;
}; // class Interface


class Manager {
public:
    explicit Manager(std::unique_ptr<Interface>&&);

    ~Manager();

private:
    std::unique_ptr<Interface> _interface;
}; // class Manager

Manager::~Manager() {
    _interface->finalize();
}
类接口{
公众:
朋友班经理;
私人:
虚拟void finalize()=0;
}; // 类接口
班级经理{
公众:
显式管理器(std::unique_ptr&&);
~Manager();
私人:
std::唯一的ptr接口;
}; // 班级经理
经理::~Manager(){
_接口->完成();
}
诀窍?在调用
finalize()
时,
接口的销毁尚未开始!对析构函数的调用将在稍后发生;这样你就不会遭受半死物体的命运

在回答这个问题之前,我将发出一个警告,警告您现在在析构函数中加入一个线程。请注意,在堆栈展开的情况下会自动调用析构函数,因此在失败时无限期等待可能是危险的;特别是如果线程正在等待当前正在解卷的线程提供的数据。。。一个典型的死锁案例


参考文献(n3337):

§12.7/4成员函数,包括虚拟函数(10.3),可在构造或销毁过程中调用(12.6.2)。当从构造函数或析构函数直接或间接调用虚函数时,包括在类的非静态数据成员的构造或销毁过程中,调用应用的对象是正在构造或销毁的对象(称为x),调用的函数是构造函数或析构函数类中的最终重写器,而不是在派生类中重写它的函数

§10.4/6成员函数可以从抽象类的构造函数(或析构函数)调用;对于从此类构造函数(或析构函数)创建(或销毁)的对象,直接或间接对纯虚拟函数进行虚拟调用(10.3)的效果尚未定义


不能从析构函数调用虚拟函数。好吧,你可以,但结果不会是你所期望的。所以别这么做,你能帮我吗