2017-08-17 12 views
3

に私は、この基本クラスを持っている:純粋仮想関数は、ベース/派生クラス

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

と、この派生クラス:Baseクラスのコンストラクタで

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

私はどのスレッドを開始していますソケットから読み取り、派生クラスで呼び出されるon_next_item()を呼び出します。 Baseデストラクタでは、リーダースレッドはアトミックフラグによって停止します。 しかし、read_loopはまだon_next_itemと呼ばれ、「純粋な仮想関数が呼び出されました!」というメッセージが表示されることがあります。エラー。

サブクラス(オブジェクト)は既に破棄されているため、関数はそれ以上登録されていません。

この競合状態を解決する適切な方法はありますか?完全のために

は、ここでは、読者のループです:

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

実行されているフラグはBaseクラスのデストラクタで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()'は、 'On_next_item()'を呼び出すスレッドが 'Derived 'd 'の一部が破壊されます。 – YSC

+0

@Soccertrashコンストラクタから起動されたthead関数から仮想関数を呼び出しています。仮想関数は、 'Derived'コンストラクタの前または後に呼び出すことができます。それはさらに悪いレースです。 – rodrigo

0

を、私は、次のchanges-

「実行中」フラグtを設定し
  1. を示唆していますo〜false(内部)〜Base()ではなくDerived()です。 「実行中」のアクセスレベルをprotectedに変更します。

    virtual〜Derived() { running = false; }

  2. (running.load()){
    on_next_item(要素)しながら、読者のループから

    を睡眠を削除します。 }

関連する問題