Arman_ 2023-07-09 18:11 采纳率: 28.6%
浏览 18
已结题

C++ lamdba值捕获引起的问题

问题背景:

  1. TaskProcessor.h类会在内部创建一个线程。将任务路由到这个线程处理。
  2. 这个问题我已经知道将 “SetListener(TestListener* listener)”方法中的lamdba表达改为[this, listener]() ,通过值捕获就能正常运行

问题:如下这段C++代码运行会报错。报错行数是"test_listener_->OnReceiver();"。错误信息有两个

  1. error: <user expression 1>:1:1: use of non-static data member 'test_listener_' of 'TestManager' from nested type ''
  2. EXC_BAD_ACCESS (code=1, address=0x7669656365526e5f)

我就想知道我写成按照引用捕获为啥会出错。出错的原因是什么?而且在使用 test_listener_ 代理前我都打印了地址,都是正确的。感谢您的指点 感激不尽 祝您工作顺利


#include <iostream>
#include "TaskProcessor.h"
using namespace CallPlugin;

class TestListener {
public:
    virtual ~TestListener() {}
    virtual void OnReceiver() = 0;
};


class TestManager {
public:
    TestManager():task_processor_(std::make_unique<TaskProcessor>()) {
        std::cout << "TestManager()" << std::endl;
    }

    ~TestManager() {
        std::cout << "~TestManager()" << std::endl;
    }

    void SetListener(TestListener* listener) {
        task_processor_->SyncTask(FROM_HERE, [&](){
            test_listener_ = listener;
            std::cout << "-----SetListener-->>test_listener_: "<<(void *)test_listener_<< std::endl;
        });
    }

    void OnReceiver() {
        task_processor_->SyncTask(FROM_HERE, [&](){
            std::cout << "-----OnReceiver-->>test_listener_: "<<(void *)test_listener_<< std::endl;
            test_listener_->OnReceiver();
        });
    }

private:
    TestListener* test_listener_;
    std::unique_ptr<TaskProcessor> task_processor_;
};


class ListenerImpl : public TestListener {
public:
    ListenerImpl() {
        std::cout<<"ListenerImpl()"<<std::endl;
    }

    ~ListenerImpl() override {
        std::cout<<"~ListenerImpl()"<<std::endl;
    }
    void OnReceiver() override {
        std::cout<<"ListenerImpl-->OnReceiver"<<std::endl;
    }
};

int main() {
    ListenerImpl* listenerImpl = new ListenerImpl();
    TestManager testManager;
    testManager.SetListener(listenerImpl);
    testManager.OnReceiver();

    std::cout << "sleep_for"<< std::endl;

    std::this_thread::sleep_for(std::chrono::milliseconds(10000));

    delete listenerImpl;

    std::cout << "Hello, World!" << std::endl;
    return 0;
};
  • 写回答

1条回答 默认 最新

  • IT论之程序员 2023-07-10 18:22
    关注

    在lambada表达式中使用[&]进行按引用捕获会导致test_listener_的生命周期被延长,从而导致错误。
    具体来说,[&]表示捕获外部作用域中的所有变量,并以引用的方式捕获。这会导致lambada表达式中捕获的变量的生命周期被延长到lambda表达式的生命周期。
    但是在本例中,test_listener_是一个指针,它指向的对象ListenerImpl是在堆上分配的,由main函数负责delete释放。而lambda表达式捕获了test_listener_指针,使其生命周期延长,导致在main函数delete listenerImpl之后,test_listener_指针成为了悬垂指针,从而出现错误。
    解决方法是不要使用[&]进行按引用捕获,可以用[=]进行按值捕获,或者指定只捕获test_listener_指针,如[test_listener_]。这样就能避免test_listener_的生命周期被无意间延长。
    另外在C++中,使用new分配的对象最好结合智能指针来管理生命周期,避免手工delete带来的问题。
    所以综合来说,建议的修改方案是:

    1. 使用智能指针管理ListenerImpl对象
    2. 在lambada表达式中按值捕获或者只捕获test_listener_,不要全引用捕获
      这样可以避免生命周期管理方面的问题。
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

问题事件

  • 系统已结题 7月22日
  • 已采纳回答 7月14日
  • 修改了问题 7月9日
  • 创建了问题 7月9日

悬赏问题

  • ¥100 任意维数的K均值聚类
  • ¥15 stamps做sbas-insar,时序沉降图怎么画
  • ¥15 unity第一人称射击小游戏,有demo,在原脚本的基础上进行修改以达到要求
  • ¥15 买了个传感器,根据商家发的代码和步骤使用但是代码报错了不会改,有没有人可以看看
  • ¥15 关于#Java#的问题,如何解决?
  • ¥15 加热介质是液体,换热器壳侧导热系数和总的导热系数怎么算
  • ¥100 嵌入式系统基于PIC16F882和热敏电阻的数字温度计
  • ¥15 cmd cl 0x000007b
  • ¥20 BAPI_PR_CHANGE how to add account assignment information for service line
  • ¥500 火焰左右视图、视差(基于双目相机)