Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pure virtual function called C++ in base/derived class

I have this base class:

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

and this derived class:

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

In the Base class constructor I'm starting a thread which reads from a socket and calls on_next_item() which is called on the derived class. In the Base destructor the reader thread is stopped via a atomic flag. But sometimes the read_loop still calls on_next_item and I get a "Pure virtual function called!" error. I assume I'm running in a race condition:

The subclass (object) has already been destructed and thus the function is not registered any more.

Is there a proper way to solve this race condition?

For completeness here's the reader loop:

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

The running flag is switched to false in the Base class destructor.

Edit (complete running example, has to be executed several times to run in the 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));
}

Thank you!

like image 940
Soccertrash Avatar asked Jan 18 '26 10:01

Soccertrash


1 Answers

Calling virtual functions from constructors or destructors is generally considered a bad idea. The function call will actually be done as if the function were not virtual because at that point the constructor of Derived has not yet been called, member variables or Derived are still uninitialized...

The obvious solution is to move the logic of your class to a public member function and call that function just after creation of the object:

Derived d;
d.run();
like image 52
rodrigo Avatar answered Jan 21 '26 00:01

rodrigo