调用functor或函数指针时触发Segfault(段错误)问题排查
你的问题不是函数指针/std::function的使用错误,也和const&转发无关,核心原因是线程调用parse_data时使用的this指针无效(和原实例指针不一致),导致访问成员变量parse_cb时触发非法内存访问,进而引发段错误。
关键排查点
实例生命周期不匹配
你创建的DownloaderDerived dldr(parse_my_json);如果是局部变量,当它所在的作用域结束(比如主线程退出、函数返回),实例会被销毁,但工作线程可能还在运行。此时线程中访问的this指针指向已释放的内存,访问parse_cb自然会触发段错误。而直接调用parse_my_json(data)不需要访问类成员,所以能正常执行。线程传递的实例指针错误
从调用栈看,downloader_callback是静态函数,它必然需要通过某种方式获取Downloader实例指针。如果:- 传递的是临时对象的地址(比如创建线程时传递了局部对象的指针,而对象已经销毁)
- 静态管理的线程容器中存储的指针被错误修改(比如实例被移动后,旧指针未更新)
- 传递过程中发生了指针的非法转换(比如错误的类型转换导致指针偏移)
都会导致线程中使用的this指针和原实例不一致。
解决方案
确保实例生命周期覆盖线程运行周期
将DownloaderDerived实例改为动态分配(比如用std::unique_ptr或std::shared_ptr管理),或者将其声明为全局变量/类静态成员,保证线程运行期间实例始终存活。// 示例:用智能指针管理实例 auto dldr = std::make_unique<DownloaderDerived>(parse_my_json);检查线程创建时的实例传递逻辑
确认静态线程管理模块中,存储和传递的是指向存活实例的有效指针,避免传递临时对象的地址,也不要直接传递对象(基类有纯虚函数,编译会报错,但如果是通过某种方式绕过,会导致切片或指针失效)。验证静态线程容器的正确性
如果用静态容器(比如std::vector<Downloader*>)管理实例,确保实例被销毁时从容器中移除,且容器中的指针始终指向有效对象。
额外验证
可以在Downloader的构造函数和析构函数中打印实例地址,同时在parse_data中打印this指针的地址,对比两者是否一致:
Downloader::Downloader(ParseCb p) : parse_cb(p) { std::cout << "Downloader constructed at: " << this << std::endl; } Downloader::~Downloader() { std::cout << "Downloader destructed at: " << this << std::endl; } bool Downloader::parse_data(const std::string& data) { std::cout << "parse_data called with this: " << this << std::endl; // ... 原有代码 }
如果析构日志早于parse_data的日志,说明实例提前被销毁,这就是问题所在。
内容的提问来源于stack exchange,提问作者herhor67

