2014-11-06 2 views
0

Я пытаюсь дождаться, когда поток погибнет в деструкторе чистого виртуального класса. Код компилируется просто отлично и, кажется, имеет смысл. Тем не менее, следующее сообщение об ошибке при запуске его:Ожидание потока для уничтожения в деструкторе чистого виртуального класса вызывает ошибку времени выполнения

pure virtual method called 
terminate called without an active exception 
Aborted (core dumped) 

Вот моя программа:

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

class Test { 
    protected: 
     int x; 
     boost::thread th; 
    public: 
     Test(void): x(10) {}; 
     ~Test(void); 
     virtual void operator()(void); 
     void run(void); 
     virtual void a(void) = 0; 
     void wait(void); 
}; 

Test::~Test(void) { 
    this->wait(); 
} 

void Test::operator()(void) { 
    this->a(); 
    x += 10; 
    std::cout << "Current Value: " << x << std::endl; 
} 

void Test::run(void) { 
    this->th = boost::thread(&Test::operator(), this); 
} 

void Test::wait(void) { 
    this->th.join(); 
} 

class Test1 : public Test { 
    public: 
     virtual void a(void); 
}; 

void Test1::a(void) { 
    x--; 
} 

main() { 
    Test1 test; 
    test.run(); 
    //test.wait(); 
} 

Ожидаемый результат 19. Я могу получить его, поместив вызов Test::wait() в main() до объекта оставляет область, а не в деструктор. Исключение не происходит, когда чисто виртуальный метод a() удален с Test.

Мои вопросы, то есть:

  1. Какой виртуальный метод ошибка со ссылкой на так Test::a() не в цепочке вызовов деструктора в?
  2. Почему код компилируется, если существует такая неопределенность?

Я предполагаю, что ответ на один будет ответом на другой ...

P.S. В случае, если это имеет значение, я бегу г ++ (GCC) 4.9.1 20140903 (пререлиз) на Arch Linux, версия ядра 3.17.2-1 в GNU Баш, версия 4.3.30 (1) -release

ответ

2

Проблема связана с проблемой, когда вы вызываете виртуальную функцию из конструктора/деструктора. В этом случае wait() вызывается из базового деструктора - Test::~Test(), что означает, что производный класс Test1 уже разрушен, а виртуальная таблица обновлена ​​до базового класса Test. Похоже, виртуальный вызов в отдельном потоке происходит после этого, и вы получаете чистый вызов виртуального метода. Поэтому поведение похоже на то, что вы косвенно вызываете a() от деструктора Test.

Когда вы вызываете wait() явно, он ждет до того, как удаляется экземпляр test1, и он отлично работает.

Не связанный с этой проблемой, dtor класса Test должен быть виртуальным, поскольку у вас есть виртуальный метод (ы).

+0

Ничего себе. Это полностью объясняет все. Не видел этого. –

+0

Я знал о последнем вопросе о виртуальном деструкторе. В этом конкретном случае это не изменило бы/не изменило ситуацию. Это просто интересная головоломка с чистым обходным решением. –

+0

Конечно: 'Текущее значение: 19'. Проблема в вашем исходном коде состоит в том, что * часть объекта, содержащая реализованный 'a()' *, разрушается слишком рано, потому что только деструктор базового класса блокирует (ждет завершения потока). Если конструктор производного класса (который называется первым) блокирует, ничего плохого не происходит, потому что vtable не может быть возвращен к версии базового класса до завершения потока. Конечно, я имею в виду только явное 'wait()' в вашем 'main()', когда я говорю: «не вызывайте' wait() '. – Oguk

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