2015-07-17 5 views
7

Я пытаюсь создать объект service, который может запускаться (т. Е. Выполнять его функцию run()) в отдельном потоке. Это объект службыC++ Наследование: вызов виртуального метода, когда он был переопределен

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

class service : public boost::noncopyable { 
public: 
    service() : stop_(false), started_(false) { } 

    virtual ~service() { 
    stop(); 
    if (thread_.joinable()) { 
     thread_.join(); 
    } 
    } 

    virtual void stop() { stop_ = true; } 

    virtual void start() { 
    if (started_.load() == false) { 
     started_ = true; 
     thread_ = std::thread([&]() { 
     run(); 
     }); 
    } 
    } 

protected: 
    virtual void run() = 0; 

    std::atomic<bool> stop_; 

    std::atomic<bool> started_; 

    std::thread thread_; 
}; 

Я создать test класс, который наследуется от абстрактного класса и вызывается в функции main()

class test : public service { 
public: 
    test() : service() { 
    std::cout<< "CTOR" << std::endl; 
    start(); 
    } 

    ~test() { 
    std::cout<< "DTOR" << std::endl; 
    } 

protected: 
    void run() override { 
    std::cout << "HELLO WORLD" <<std::endl; 
    } 
}; 


int main() { 
    test test1; 
    return 0; 
} 

Теперь, когда я исполню это, почему я получаю ошибка pure virtual function called? Функция run() явно переопределена в классе test. Что еще хуже, что он работает правильно иногда?

$ ./a.out 
CTOR 
DTOR 
pure virtual method called 
terminate called without an active exception 

$ ./a.out 
CTOR 
DTOR 
pure virtual method called 
terminate called without an active exception 

$ ./a.out 
CTOR 
DTOR 
pure virtual method called 
terminate called without an active exception 

$ ./a.out 
CTOR 
DTOR 
HELLO WORLD 

$ ./a.out 
CTOR 
DTOR 
pure virtual method called 
terminate called without an active exception 

Что может быть неправильно здесь?

+1

Вы вызываете виртуальную функцию 'start();' в конструкторе. В этом причина этой ошибки. – Jagannath

+2

@Jagannath Это совершенно четко определено здесь и не является проблемой. – Barry

+1

Нет, это не так. На этом этапе уже построенный производный объект уже построен. –

ответ

10

Пошлите, шаг за шагом:

1) Вы строите объект.

2) Вы выполнить следующий фрагмент кода:

if (started_.load() == false) { 
    started_ = true; 
    thread_ = std::thread([&]() { 
    run(); 
    }); 
} 

Родитель поток немедленно возвращается в main() где сразу выходы и разрушает ваш объект.

Вот ваша ошибка:

  • Вы не гарантируется, что поток начал в start() собирается достичь вызова run(), выше, до того, как родительский поток завершает процесс. И дочерний поток, и родительский поток работают одновременно.

Таким образом, каждый раз, когда родительский поток уничтожает объект до того, как дочерний поток попадает на передачу, и вызывает run().

В этот момент объект, вызов которого вызван методом run(), уже уничтожен.

Неопределенное поведение.

Утверждение, которое вы нажимаете каждый раз в то время, является одним из возможных результатов этого неопределенного поведения.

+1

Должен ли дескриптор 'thread_.join()' в базовом классе 'гарантировать, что объект не будет уничтожен до запуска потока? – subzero

+2

@subzero Здесь есть три шага: (A) Деструктор производного класса (B) Выполнено тело течения (C) Деструктор базового класса. (A) определенно происходит до (C). Но ничто не предотвращает (A) раньше (B) до (C), что означает, что vtable разматывается до того, как 'run()' получил вызов до того, как 'join()' получил вызов. – Barry

+1

@subzero 'гарантировать, что объект не будет уничтожен'. В программировании MT ваша задача - контролировать жизнь объекта. Оставляя его в компиляторе, всегда возникают такие проблемы. – PaulMcKenzie

Смежные вопросы