c ++ lambda函数调用纯虚函数

时间:2018-06-02 16:01:10

标签: c++ multithreading c++11 lambda stdthread

我正在尝试为std :: thread创建一个包装类。此类提供kick方法,该方法启动线程并调用纯虚函数。我使用派生类来调用此kick方法,派生类也实现了虚函数。

class Executor
{
public:
    // constructor
    Executor();

    // destructor
    ~Executor();

    // kick thread execution
    void Kick();

private:
    // thread execution function
    virtual void StartExecution() = 0;

    // thread handle
    std::thread mThreadHandle;
};

以下是执行者类

的实现
Executor::Executor()
{
    // Nothing to be done here
}

Executor::~Executor()
{
    if (mThreadHandle.joinable())
        mThreadHandle.join();
}

void Executor::Kick()
{
    // mThreadHandle = std::thread(&Executor::StartExecution, this);
    mThreadHandle = std::thread([this] {this->StartExecution();});
}

我正在使用 Consumer 类继承此类并实现StartExecution方法。当我使用kick方法时,它显示调用的纯虚函数,程序终止。

std::unique_ptr<Consumer> consumer = std::make_unique<Consumer>();
consumer->Kick();

在执行者踢法中。我添加了断点并开始查找错误。

  

mThreadHandle = std :: thread([this] {this-&gt; StartExecution();});

两次。首先是因为kick方法,所以第二个执行lambda函数。我第一次看到这个指向消费者类。但是当谈到lambda函数时,它会搞砸, vptr 指向纯虚函数。

我会对这里的错误感兴趣而不是简单的答案

2 个答案:

答案 0 :(得分:3)

根据我的尝试进行猜测:在线程执行之前,Consumer被破坏。

我已经~Executor虚拟并为相关的函数调用添加了一些打印语句。

#include <iostream>
#include <memory>
#include <thread>
#include <chrono>

class Executor
{
public:
    // constructor
    Executor();

    // destructor
    virtual ~Executor();

    // kick thread execution
    void Kick();

private:
    // thread execution function
    virtual void StartExecution() { std::cout << "Executor::Kick\n"; }

    // thread handle
    std::thread mThreadHandle;
};

Executor::Executor()
{
    // Nothing to be done here
}

Executor::~Executor()
{
    std::cout << "~Executor\n";
    if (mThreadHandle.joinable())
        mThreadHandle.join();
}

void Executor::Kick()
{
    // mThreadHandle = std::thread(&Executor::StartExecution, this);
    mThreadHandle = std::thread([this] {this->StartExecution();});
}


class Consumer: public Executor {
public:
    ~Consumer() {
        std::cout << "~Consumer\n";
    }
private:
    virtual void StartExecution() { std::cout << "Consumer::Kick\n"; }
};

int main() {
    {
        std::cout << "1:\n";
        std::unique_ptr<Consumer> consumer = std::make_unique<Consumer>();
        consumer->Kick();
    }
    {
        std::cout << "2:\n";
        std::unique_ptr<Consumer> consumer = std::make_unique<Consumer>();
        consumer->Kick();
        std::cout << "Sleeping for a bit\n";
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
    return 0;
}

输出:

1:
~Consumer
~Executor
Executor::Kick
2:
Sleeping for a bit
Consumer::Kick
~Consumer
~Executor

See it run here

在销毁消费者之前休眠让线程运行并调用正确的函数。 “真正的”解决方案是确保消费者至少与线程本身一样长。由于线程存在于基类Executor中,因此无法保证,因为派生类在基类之前被破坏。

来自cppreference(强调我的):

  

当从构造函数或析构函数直接或间接调用虚函数时(包括在构造或销毁类的非静态数据成员期间,例如在成员初始化程序列表中),以及调用的对象apply是正在构造或销毁的对象,被调用的函数是构造函数或析构函数类中的最终覆盖,而不是在更派生的类中覆盖它。 换句话说,在构造或破坏期间,不存在更多派生的类。

当在构造/销毁期间在不同的线程中调用成员函数时,这似乎适用。

答案 1 :(得分:1)

即使在线程有机会调用该函数之前,程序也会内存不足。如果你改变你的代码

void Executor::Kick()
{
    mThreadHandle = std::thread([this] {this->StartExecution();});
    this_thread::sleep_for(chrono::seconds(1)); // any number
}

这会奏效。

这就是为什么你不能通过捕获列表

中的引用传递this的确切原因

现在谈谈您的具体问题

  
    

我会对这个问题感兴趣而不是简单的答案。

  

vPTR指向VTable,当类离开内存时,vPTR指向基类VTable,因此发生了这种情况。您可以在调用函数

之前打印vTable地址来检查相同内容