2017-08-17 130 views
3

我有这样的基类:纯虚函数派生类

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析构函数中,reader线程通过原子标志停止。 但有时read_loop仍然叫on_next_item,我得到一个“纯粹的虚函数调用!”错误。我假设我在竞赛状态下运行:

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

有没有合适的方法来解决这种竞争条件?

为了完整这里的读者循环:在Base类的析构

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

的行驶标志切换为false。

编辑(完整运行的例子,有被执行多次在这个问题上运行):

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

谢谢!

+0

您能照常提供[MCVE]吗?其他任何事情都会导致纯粹的猜测。 – user0042

+0

您可以将逻辑从'Base'的析构函数移动到成员函数。或者这不适合你? –

+0

在构造函数中创建线程似乎不是一个好主意。同样在Base类构造函数中,对象没有完全形成(派生类尚未存在)。 https://stackoverflow.com/questions/30258639/when-is-it-safe-to-call-this-in-constructor-and-destructor – mksteve

回答

4

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

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

Derived d; 
d.run(); 
+0

基本上我不会从析构函数调用虚函数。它是从一个线程上运行的方法调用的。 – Soccertrash

+0

@rodrigo同样适用于对象销毁:'Derived ::〜Derived()'应该调用'Base :: join()'方法来确保调用'on_next_item()'的线程在Derived 'd'的一部分被破坏。 – YSC

+0

@Soccertrash你从一个从构造函数开始的函数调用一个虚拟函数。虚函数可以在Derived构造函数之前或之后调用。这是一场比赛,更糟糕的是。 – rodrigo

0

我建议以下changes-

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

    virtual〜Derived() { running = false; }

  2. 从读者的回路中取出睡眠

    而(running.load()){
    on_next_item(元件); }