捕获已销毁对象所属Lambda副本引发Segfault的问题及解决方案咨询
让我来帮你理清这个段错误的根源——这其实是对Lambda按值捕获的一个常见误解!
为什么会触发Segfault?
在你的bla::operator*成员函数中,你用了[=]按值捕获,看起来会复制所有需要的变量,但这里有个关键细节:在类的成员函数里,this是一个指针,[=]捕获的是这个指针的副本,而不是*this对象的完整副本。
看你的operator*实现:
bla operator*(const bla other) const{ return bla([=](int a){return this->func_(a)*other.func_(a);}); }
这里的Lambda捕获了this指针(指向原来的bla对象)和other参数(已经是原对象的副本,这部分没问题)。当你在blub::multiply()中执行this->blas_ = newblas;时,原来的bla(1)和bla(2)对象会被销毁,此时Lambda里的this指针就变成了悬空指针。后续调用func_(10)时,会通过悬空指针访问已经被释放的内存,自然触发段错误。
你看到的帖子说按值捕获会生成完整副本,那是针对普通变量或引用的情况,但指针的按值捕获只会复制指针本身,不会复制它指向的对象——这是很容易踩的坑!
如何修复?
我们需要让Lambda不依赖原bla对象的存在,直接持有std::function的副本,有两种简单的方案:
方案1:直接捕获func_(推荐)
修改operator*的Lambda捕获列表,显式捕获两个func_对象,这样Lambda就完全独立于原bla对象:
class bla{ public: bla(int n){ func_ = [n](int a){return n;}; } bla(std::function<int(int)> f){ func_ = f; } bla operator*(const bla other) const{ // 直接捕获两个func_的副本,不再依赖this指针 return bla([func1 = this->func_, func2 = other.func_](int a){ return func1(a) * func2(a); }); } std::function<int(int)> func_; };
这样Lambda内部持有两个std::function的独立副本,原bla对象销毁后,Lambda依然可以正常工作。
方案2:C++17+ 捕获*this按值
如果你使用的是C++17或更高版本,可以通过[=, *this]直接捕获当前对象的副本,这样Lambda会持有*this的完整拷贝,不再依赖原对象:
bla operator*(const bla other) const{ return bla([=, *this](int a){ return this->func_(a) * other.func_(a); }); }
不过这种方式会捕获整个bla对象,而我们其实只需要func_,所以方案1更高效、更直接。
验证修复效果
修改后运行你的测试代码:
int main() { blub myblub = blub({bla(1), bla(2)}); std::cout << myblub.blas_[0].func_(10); // 输出1 myblub.multiply(); std::cout << myblub.blas_[0].func_(10); // 输出2,不再触发段错误 }
此时新的Lambda里的func1和func2都是独立的副本,原对象销毁后不会影响它们的正常执行。
内容的提问来源于stack exchange,提问作者Towdo

