0

Я написал этот простой пример, чтобы получить Что происходит я хочу, чтобы все логики должны быть реализованы в базовом классе и получены конкретные методы в производном классе, так:C++ называть

#include <thread> 
#include <atomic> 
#include <chrono> 
#include <iostream> 

class base 
{ 
public: 
    base() : stop(false) {} 

    virtual ~base() 
    { 
     std::cout << "Destructor base\n"; 

     if(handle.joinable()) 
     { 
      stop = true; 
      handle.join(); 
     } 
    } 

    void runThread() { handle = std::thread(worker, this); } 

    virtual void stopThread() { std::cout << "Base stopThread\n"; } 

protected: 
    std::atomic<bool> stop; 

    std::thread handle; 

    static void worker(base *me) 
    { 
     while(me->stop == false) 
     { 
      std::cout << "Working\n"; 
      me->stopThread(); // this one in called as derived 
      std::this_thread::sleep_for(std::chrono::seconds(1)); 
     } 

     me->stopThread(); // this one is called as base 
    } 
}; 

class derived : public base 
{ 
public: 

    ~derived() 
    { 
     std::cout << "Destructor derived\n"; 
    } 

private: 
    void stopThread() { std::cout << "derived stopThread\n"; } 
}; 

int main() 
{ 
    derived der; 
    der.runThread(); 

    std::this_thread::sleep_for(std::chrono::seconds(3)); 
} 

и Результат:

Working 
derived stopThread 
Working 
derived stopThread 
Working 
derived stopThread 
Destructor derived 
Destructor base 
Base stopThread <-- hmmm 

Итак, все работает отлично, кроме базового деструктора - он вызывает базу :: stopThread. Почему это происходит? Если я сделаю это виртуальным, я получу исключение.

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

Где я ошибаюсь?

Благодаря

+6

Ничего общего с потоками: динамический тип объекта внутри его деструктора является его статическим типом, поскольку любая производная часть уже уничтожена. – Quentin

+3

Кстати, факт, что чистая виртуальная функция дал вам исключение, должен был намекнуть вам. – SergeyA

+0

Как примечание, правила меняются в зависимости от языков, поэтому вам нужно изучить правила для своего языка, если хотите сделать некоторые продвинутые вещи. – Phil1970

ответ

4

виртуальные методы не рассылать наиболее производном реализации в конструктор или деструктор. Причина этого в том, что к тому моменту, когда деструктор base был вызван, экземпляр derived уже уничтожен.

Более формально, как сказал Квентин в комментариях, виртуальные методы отправляют статический тип экземпляра внутри конструктора или деструктора.

+0

«Причина этого в том, что к тому времени, когда деструктор базы был вызван, производный экземпляр уже был уничтожен». Я думаю, что для ясности вы можете добавить к этому следующее: «И наоборот, когда был вызван конструктор« base », часть« производная »еще не построена. –

+0

поэтому единственный способ разрешить это - переместить деструктор в производный класс? –

+1

@FedorKryukov, вам может потребоваться переосмыслить ваш дизайн сейчас, зная, что (иногда довольно неудобный) факт о неполиморфных вызовах от конструктора/деструктора. В любом случае объединение нити в деструкторе не обязательно является лучшей идеей. – SergeyA