【问题标题】:C++ virtual destructor cause base method to be calledC++ 虚拟析构函数导致基方法被调用
【发布时间】:2016-10-15 09:46:03
【问题描述】:

我写了这个简单的例子来了解发生了什么 我希望所有逻辑都在基类中实现,并在派生类中派生特定方法,所以:

#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 

所以除了基本析构函数之外一切都正常 - 它调用 base::stopThread。为什么会这样?如果我把它变成纯虚拟的,我会得到一个例外。

我可以通过将析构函数移动到派生类来解决这个问题,但这不清楚,因为我希望所有逻辑都在基类中实现。

我哪里错了?

谢谢

【问题讨论】:

  • 与线程无关:对象在其析构函数中的动态类型是其静态类型,因为任何派生部分都已被销毁。
  • 顺便说一句,纯虚函数给你一个例外的事实应该已经暗示了你。
  • 附带说明,规则因语言而异,因此如果你想做一些高级的事情,你必须学习你的语言规则。
  • 这种设计在任何语言中都不好; base 代表什么?抽象是什么?
  • @curiousguy 一些具有轮询逻辑和接口的设备类,派生的是不同的版本和型号。当软件停止时,它应该被正确禁用

标签: c++ multithreading polymorphism destructor virtual-functions


【解决方案1】:

虚拟方法不会分派到构造函数或析构函数中最派生的实现。原因是当base 的析构函数被调用时,derived 实例已经被销毁了。

更正式地说,正如 Quentin 在 cmets 中所说,虚方法在构造函数或析构函数内分派到实例的静态类型。

【讨论】:

  • "原因是在调用 base 的析构函数时派生实例已经被销毁。"我认为为了清楚起见,您可以添加:“相反,当base 的构造函数被调用时,derived 部分尚未构造。
  • 所以解决这个问题的唯一方法是将析构函数移动到派生类?
  • @FedorKryukov,您现在可能需要重新考虑您的设计,因为知道(有时很不方便)关于来自构造函数/析构函数的非多态调用的事实。无论如何,在析构函数中加入线程不一定是最好的主意。
  • @FedorKryukov - 您可以将所有停止逻辑移动到基类中,这样您就可以从析构函数中调用它。
  • @Sean 在我的项目中这是不可能的,因为派生类使用不同的停止命令实现各种硬件设备,所以我将从构造它们的对象进行连接。谢谢!
猜你喜欢
  • 1970-01-01
  • 2017-08-13
  • 1970-01-01
  • 2014-10-02
  • 1970-01-01
  • 2013-11-01
  • 2011-08-12
  • 2012-09-21
  • 2015-12-15
相关资源
最近更新 更多