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

标签 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();});
}

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

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

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

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

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

我会对其中的错误感兴趣,而不是简单的回答

最佳答案

只是基于我所尝试的猜测:您的 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 (强调我的):

When a virtual function is called directly or indirectly from a constructor or from a destructor (including during the construction or destruction of the class’s non-static data members, e.g. in a member initializer list), and the object to which the call applies is the object under construction or destruction, the function called is the final overrider in the constructor’s or destructor’s class and not one overriding it in a more-derived class. In other words, during construction or destruction, the more-derived classes do not exist.

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

关于C++ lambda函数调用纯虚函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50658692/

相关文章:

C++ setenv 解析其他变量

c++ - fmod 还是不 fmod?

c++ - 为什么这个 C++ 程序会出现段错误?

c++ - MFC 消息队列限制

objective-c - 此 View 动画概念的正确干净且线程安全的设计

java - 获取锁对象线程正在等待

c++ - cout vector<vector<int>> 更简洁

c++ - 如何禁用缩小转换警告?

c++ - QtCreator:将字符串打印到终端的最佳方法是什么?

c++ - 列表初始化期间临时对象的生命周期