【问题标题】:Pure virtual function called C++ in base/derived class基类/派生类中称为 C++ 的纯虚函数
【发布时间】:2018-01-25 12:24:43
【问题描述】:

我有这个基类:

class Base {
public:
  Base();
  virtual ~Base();
protected:
  virtual on_next_item(std::string& item) = 0;
private:
    void read_loop();
};

还有这个派生类:

class Derived : public Base {
public:
  Derived();
  virtual ~Derived();
protected:
  void on_next_item(std::string& item) override;
};

Base 类构造函数中,我正在启动一个线程,该线程从套接字读取并调用在派生类上调用的on_next_item()。 在Base 析构函数中,读取器线程通过原子标志停止。 但有时read_loop 仍然调用on_next_item,我得到一个“调用纯虚函数!”错误。我假设我在比赛条件下跑步:

子类(对象)已被破坏,因此不再注册该函数。

有解决这种竞争条件的正确方法吗?

为了完整起见,这里是阅读器循环:

while (running.load())
{
  string item = read();
  if (running.load())
  {
    on_text_item(item);
  }
}

Base 类析构函数中的运行标志切换为 false。

编辑(完整的运行示例,在issue中必须执行多次才能运行):

#include <atomic>
#include <boost/thread/thread.hpp>
#include <boost/chrono.hpp>
#include <iostream>

class Base
{
public:
  Base() : running(true)
  {
    readerThread = new boost::thread(&Base::read_loop, this);
  }

  virtual ~Base()
  {
    running = false;

    delete readerThread;
    readerThread = nullptr;
  }

protected:
  virtual void on_next_item(std::string &item) = 0;

private:
  boost::thread *readerThread;

  void read_loop()
  {
    std::string element = "Element";
    while (running.load())
    {
      boost::this_thread::sleep_for(boost::chrono::milliseconds(2));
      on_next_item(element);
    }
  }

  std::atomic_bool running;
};

class Derived : public Base
{
public:
  Derived() : Base()
  {
  }

  virtual ~Derived()
  {
  }

protected:
  virtual void on_next_item(std::string &item)
  {
    std::cout << "On Next Item " << item << std::endl;
  }
};

void constAndDestruct()
{
  Derived d;
  boost::this_thread::sleep_for(boost::chrono::seconds(2));
}

int main(int argc, char **argv)
{

  constAndDestruct();
  boost::this_thread::sleep_for(boost::chrono::seconds(2));
}

谢谢!

【问题讨论】:

  • 能否像往常一样提供minimal reproducible example。其他任何事情都会导致纯粹的猜测。
  • 您可以将逻辑从Base 的析构函数移到成员函数中。或者这对你不起作用?
  • 在构造函数中创建线程似乎不是一个好主意。同样在基类构造函数中,对象还没有完全形成(派生还不存在)。 stackoverflow.com/questions/30258639/…
  • derived part is destructed before the base destructor gets called.该功能是否需要是纯虚拟的?如果您使基本实现成为无操作,那么如果在派生部分被破坏后调用它就不是问题
  • @Soccertrash 是的,这就是我的意思:virtual void on_next_item(std::string &amp;item) { }

标签: c++ multithreading


【解决方案1】:

从构造函数或析构函数调用虚函数是generally considered a bad idea。函数调用实际上会像函数不是虚拟函数一样完成,因为此时尚未调用 Derived 的构造函数,成员变量或 Derived 仍未初始化...

显而易见的解决方案是将类的逻辑移至公共成员函数,并在创建对象后立即调用该函数:

Derived d;
d.run();

【讨论】:

  • 基本上我不会从析构函数中调用虚函数。它是从在线程上运行的方法调用的。
  • @rodrigo 对象销毁也是如此:Derived::~Derived() 应该调用Base::join() 方法以确保调用on_next_item() 的线程在Derivedd 部分之前停止被破坏了。
  • @Soccertrash 您正在从构造函数启动的thead 函数调用虚函数。可以在Derived 构造函数之前或之后调用虚函数。这是一场比赛,甚至更糟。
【解决方案2】:

我建议进行以下更改-

  1. 在 ~Derived() 而不是 ~Base() 中将“运行”标志设置为 false。 将“运行”的访问级别更改为受保护。

    虚拟的~Derived() { 运行=假; }

  2. 从阅读器的循环中移除睡眠

    while (running.load()) {
    on_next_item(元素); }

【讨论】:

    猜你喜欢
    • 2018-10-14
    • 1970-01-01
    • 2018-05-02
    • 1970-01-01
    • 1970-01-01
    • 2015-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多