Lambda 函数捕获错误的 "this" 指针
Lambda function captures wrong "this" pointer
我在制作一个涉及 lambda 表达式和共享指针的普通 C++ 项目时遇到了问题。该项目位于 Visual Studio、Debug、x64 中。这是我的代码。
Class1.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&);
};
Class2.h:
#pragma once
#include "Class1.h"
class Class2
{
Class1 m_class1obj;
public:
Class2(int, int);
void Assign(const int&);
int Compute(int);
};
main.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 在 Class1
对象的构造时间捕获 this
指针时,它记录了Class2
的智能指针创建后与对象地址不同的地址!似乎第一个记录的地址已经过时了。当我调用 class2_ptr->Compute(23)
时,我最终取消引用 int val = *m_data_ptr;
处的空指针,从而导致崩溃,即使我在调用 [=23= 之前在 class2_ptr->Assign(val);
中分配了一个非空地址]!但是为什么this
的地址变了呢?这是因为对用户隐藏了内存中对象的内部重新分配?如果是这样,为什么编译器没有在 m_func
的存储中重新分配 this
的正确值?此外,尽管在 m_func
中记录了 Class1
对象的错误地址,但在 m_func
.
中使用正确的值访问了另一个数据成员 m_data
一件重要的事情是,如果我注释掉main()中的最后四行并删除其他三行的注释,程序就不会崩溃!显然,使用共享指针与此有关。
这种情况下是不是lambda表达式或智能指针使用不当有问题?我似乎无法在 C++ 标准中找到适合我的情况的解释。
感谢所有解释发生了什么的评论!
std::make_shared<Class2>(Class2(3, 5))
创建临时 Class2
,将其移入 shared_ptr
存储,然后销毁原始文件。
您的 lambda 捕获该临时文件的地址,并在临时文件被销毁后使用它。
您需要避免创建临时文件并直接在 shared_ptr
存储中构造 Class2
:std::make_shared<Class2>(3, 5);
.
或者更好的是,去掉 lambda。我不确定你为什么要在那里使用它。
编译器按照您的指示进行操作。
If so, why didn't the compiler reassign the proper value of this in the storage of 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
的内部存储了一个 pointer-to-yourself 并且你在复制时从不修复它。
这会导致您的代码无法编译;修复使其编译并删除错误:
class2_ptr = std::make_shared<Class2>(3, 5);
原来创建了一个临时的Class2
,然后复制到shared 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
的复制构造函数,也不必更改构造它的方式。
我在制作一个涉及 lambda 表达式和共享指针的普通 C++ 项目时遇到了问题。该项目位于 Visual Studio、Debug、x64 中。这是我的代码。
Class1.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&);
};
Class2.h:
#pragma once
#include "Class1.h"
class Class2
{
Class1 m_class1obj;
public:
Class2(int, int);
void Assign(const int&);
int Compute(int);
};
main.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 在 Class1
对象的构造时间捕获 this
指针时,它记录了Class2
的智能指针创建后与对象地址不同的地址!似乎第一个记录的地址已经过时了。当我调用 class2_ptr->Compute(23)
时,我最终取消引用 int val = *m_data_ptr;
处的空指针,从而导致崩溃,即使我在调用 [=23= 之前在 class2_ptr->Assign(val);
中分配了一个非空地址]!但是为什么this
的地址变了呢?这是因为对用户隐藏了内存中对象的内部重新分配?如果是这样,为什么编译器没有在 m_func
的存储中重新分配 this
的正确值?此外,尽管在 m_func
中记录了 Class1
对象的错误地址,但在 m_func
.
m_data
一件重要的事情是,如果我注释掉main()中的最后四行并删除其他三行的注释,程序就不会崩溃!显然,使用共享指针与此有关。 这种情况下是不是lambda表达式或智能指针使用不当有问题?我似乎无法在 C++ 标准中找到适合我的情况的解释。
感谢所有解释发生了什么的评论!
std::make_shared<Class2>(Class2(3, 5))
创建临时 Class2
,将其移入 shared_ptr
存储,然后销毁原始文件。
您的 lambda 捕获该临时文件的地址,并在临时文件被销毁后使用它。
您需要避免创建临时文件并直接在 shared_ptr
存储中构造 Class2
:std::make_shared<Class2>(3, 5);
.
或者更好的是,去掉 lambda。我不确定你为什么要在那里使用它。
编译器按照您的指示进行操作。
If so, why didn't the compiler reassign the proper value of this in the storage of 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
的内部存储了一个 pointer-to-yourself 并且你在复制时从不修复它。
这会导致您的代码无法编译;修复使其编译并删除错误:
class2_ptr = std::make_shared<Class2>(3, 5);
原来创建了一个临时的Class2
,然后复制到shared 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
的复制构造函数,也不必更改构造它的方式。