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

the*_*dal 5 c++ lambda multithreading c++11 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;
};
Run Code Online (Sandbox Code Playgroud)

下面是executor类的实现

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();});
}
Run Code Online (Sandbox Code Playgroud)

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

std::unique_ptr<Consumer> consumer = std::make_unique<Consumer>();
consumer->Kick();
Run Code Online (Sandbox Code Playgroud)

在 executor kick 方法中。我添加了一个断点并开始寻找问题所在。它来到

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

线两次。首先是因为 kick 方法,其次是执行 lambda 函数。我第一次看到this指向Consumer类。但是当涉及到 lambda 函数时,它就搞砸了,vptr指向纯虚函数。

我会对这有什么问题而不是简单的答案感兴趣

Kev*_*vin 5

只是基于我尝试过的猜测:您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;
}
Run Code Online (Sandbox Code Playgroud)

输出:

1:
~Consumer
~Executor
Executor::Kick
2:
Sleeping for a bit
Consumer::Kick
~Consumer
~Executor
Run Code Online (Sandbox Code Playgroud)

看到它在这里运行

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

来自cppreference(强调我的):

当从构造函数或析构函数直接或间接调用虚函数时(包括在类的非静态数据成员的构造或销毁期间,例如在成员初始值设定项列表中),并且该调用适用的对象是正在构造或销毁的对象,被调用的函数是构造函数或析构函数类中的最终覆盖程序,而不是在派生程度更高的类中覆盖它。换句话说,在构造或销毁期间,派生程度更高的类不存在。

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