Lambda 函数捕获错误的指针"this"
Lambda function captures wrong "this" pointer
我在制作一个涉及lambda表达式和共享指针的琐碎C++项目时遇到了问题。该项目位于 Visual Studio、Debug、x64 中。这是我的代码。
1.h类:
#pragma once
#include <functional>
class Class1
{
int m_data;
const int* m_data_ptr;
public:
std::function<int(int)> m_func;
Class1(int, int);
void Assign(const int&);
};
类2.h:
#pragma once
#include "Class1.h"
class Class2
{
Class1 m_class1obj;
public:
Class2(int, int);
void Assign(const int&);
int Compute(int);
};
主.cpp:
#include <iostream>
#include "Class1.h"
#include "Class2.h"
Class1::Class1(int i, int j) : m_data(j), m_data_ptr(nullptr)
{
m_func = [i, this](int x)
{
int val = *m_data_ptr;
return (val + m_data + i)*x;
};
std::cout << "Creating class1 object!n";
}
void Class1::Assign(const int& v)
{
m_data_ptr = &v;
}
Class2::Class2(int i, int j) : m_class1obj(i, j)
{
std::cout << "Creating class2 object!n";
}
void Class2::Assign(const int& v)
{
m_class1obj.Assign(v);
}
int Class2::Compute(int v)
{
return m_class1obj.m_func(v);
}
int main()
{
int val = 4;
/*
Class2 class2obj(3, 5);
class2obj.Assign(val);
std::cout << class2obj.Compute(23.0) << std::endl;
*/
std::shared_ptr<Class2> class2_ptr;
class2_ptr = std::make_shared<Class2>(Class2(3, 5));
class2_ptr->Assign(val);
std::cout << class2_ptr->Compute(23) << std::endl;
}
代码编译正常,但在执行最后一行main()
时崩溃。在调试时,我发现问题在完成行class2_ptr = std::make_shared<Class2>(Class2(3, 5));
时会自行发现 出于某种原因,当第m_func = ...
行中的 lambda 在对象的构造时捕获this
指针时Class1
它会记录在创建Class2
的智能指针后立即与对象的地址不同的地址!第一个记录的地址似乎已经过时了。当我调用class2_ptr->Compute(23)
时,我最终在int val = *m_data_ptr;
处取消引用空指针,从而导致崩溃,即使我在调用m_func
之前在class2_ptr->Assign(val);
中分配了一个非空地址!但是为什么this
的地址发生了变化?这是因为内存中对用户隐藏的对象进行了内部重新分配?如果是这样,为什么编译器没有在m_func
存储中重新分配正确的this
值?此外,尽管在m_func
中重新编码了错误的Class1
对象的地址,m_data
m_func
中以正确的值访问另一个数据成员。
重要的是,如果我注释掉main()中的最后四行并删除其他三行的注释,程序不会崩溃!显然,使用共享指针与此有关。 在这种情况下,不当使用 lambda 表达式或智能指针是否存在问题?我似乎无法在C++标准中找到我的情况的解释。
感谢您解释正在发生的事情的所有评论!
std::make_shared<Class2>(Class2(3, 5))
创建一个临时Class2
,该被移动到shared_ptr
存储中,然后原始文件被销毁。
您的 lambda 会捕获该临时地址,并在临时地址被销毁后使用它。
您需要避免直接在shared_ptr
存储中创建临时和构造Class2
:std::make_shared<Class2>(3, 5);
。
或者更好的是,摆脱lambda。我不确定你为什么要在那里使用它。
编译器按照你的要求做了。
如果是这样,为什么编译器没有在m_func存储中重新分配它的正确值?
因为你从来没有告诉过它?
[i, this](int x)
{
int val = *m_data_ptr;
return (val + m_data + i)*x;
};
this
的值在您创建 Lambda 的那一刻捕获。
除了在使用成员变量时取消引用它之外,不会发生任何魔术。
然后,您将m_func
从一个对象复制到另一个对象;lambda 中的指针仍然指向旧对象。 然后销毁旧对象。 然后你打电话给m_func
.
复制构造函数已损坏。 删除它:
class Class1 {
Class1(Class1 const&)=delete;
因为你在m_func
的内心存储了一个指向自己的指针,并且在复制时永远不会修复它。
这会导致您的代码无法编译;修复程序使其编译并删除错误:
class2_ptr = std::make_shared<Class2>(3, 5);
原始版本创建了一个临时Class2
,然后将其复制到共享 PTR 中;此版本直接就地创建了一个。
另一种方法是修复损坏的m_func
:
std::function<int(Class1*, int)> m_func;
和
m_func = [i](Class1* self, int x)
{
int val = *self->m_data_ptr;
return (val + self->m_data + i)*x;
};
以及使用它的位置:
return m_class1obj.m_func(&m_class1obj, v);
现在,您不再需要=delete
Class1
的复制 CTOR,也不必更改构建它的方式。
- 为什么使用 "this" 指针调用派生成员函数?
- 关于C++中具有多重继承"this"指针的说明
- Doees the 'this' 指针参与虚函数的多态行为
- 指针'this+1' C++中指的是什么?
- 为什么使用指向函数的指针调用虚函数时不需要指针"this"?
- Lambda 函数捕获错误的指针"this"
- 调用类的方法时,类的指针"this"变为 null C++
- 指针'this'可以是共享指针吗?
- 如何在C++中为指针'this'赋值
- C++:指针"this"没用吗?
- 指针"this"有什么问题?
- 将指针'this'传递为 LPARAM
- 错误 C2663:指针'this'重载没有合法转换
- C++ : 了解指针"this"
- 更改对象的指针'this'以指向不同的对象
- C++编译器是为所有成员方法生成指针"this"还是仅为引用成员的方法生成指针?
- 使用指针和不使用指针"this"有区别吗?
- 将enable_shared_from_这个基于类的指针(this)存储在itelf中
- 创建指向对象的指针"this"
- 如何在类中取消初始化指针"this"?