[英]Lambda function captures wrong “this” pointer
I'm having a problem with making one trivial C++ project involving lambda-expressions and shared pointers to work. 我在制作一个包含lambda表达式和共享工作指针的普通C ++项目时遇到了问题。 The project is in Visual Studio, Debug, x64.
该项目在Visual Studio,Debug,x64中。 This is the code I have.
这是我的代码。
Class1.h: 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: 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: 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;
}
The code compiles fine, but crashes while executing the last line of main()
. 代码可以正常编译,但是在执行
main()
的最后一行时会崩溃。 While debugging, I found that the problem discovers itself upon completion of the line class2_ptr = std::make_shared<Class2>(Class2(3, 5));
在调试时,我发现问题是在完成
class2_ptr = std::make_shared<Class2>(Class2(3, 5));
For some reason, when lambda in line m_func = ...
captures the this
pointer at Class1
object's construction time, it records the address that becomes different from the object's address right after the smart pointer for Class2
has been created! 出于某种原因,当
m_func = ...
行中的lambda在Class1
对象的构造时间捕获this
指针时,它会在创建Class2
的智能指针后立即记录与该对象的地址不同的地址! It seems the first recorded address becomes outdated. 似乎第一个记录的地址已过时。 When I call
class2_ptr->Compute(23)
, I end up dereferencing a null pointer at int val = *m_data_ptr;
当我调用
class2_ptr->Compute(23)
,我最终在int val = *m_data_ptr;
处取消了对空指针的引用int val = *m_data_ptr;
, thus causing the crash, even though I assigned a non-null address in class2_ptr->Assign(val);
,即使我在
class2_ptr->Assign(val);
分配了一个非空地址,也因此导致崩溃class2_ptr->Assign(val);
prior to calling m_func
! 在调用
m_func
之前! But why did the address of this
change? 但是为什么地址
this
变化? It is because of internal reallocation of the object in memory hidden from a user? 是因为对象的内部重新分配对用户隐藏了吗? If so, why didn't the compiler reassign the proper value of
this
in the storage of m_func
? 如果是这样,为什么没有编译器重新分配的正确值
this
在存储m_func
? Also, despite having the wrong address of the object of Class1
recored in m_func
, the other data member m_data
is accessed with correct value in m_func
. 同样,尽管在
m_func
记录了Class1
对象的错误地址,但仍使用m_data
中的正确值访问了其他数据成员m_func
。
An important thing is that if I comment out the last four lines in main() and remove comments for the other three lines, the program does not crash! 重要的是,如果我注释掉main()的最后四行并删除其他三行的注释,则程序不会崩溃! Clearly, using shared pointer has something to do with it.
显然,使用共享指针与它有关。 Is there a problem in improper use of lambda expressions or smart pointers in this case?
在这种情况下,不正确使用lambda表达式或智能指针是否存在问题? I can't seem to find the explanation in the C++ standard for my situation.
我似乎无法在C ++标准中找到适合我的情况的解释。
Thanks for all the comments explaining what's going on! 感谢您对所有解释一切的评论!
std::make_shared<Class2>(Class2(3, 5))
creates a temporary Class2
, which is moved into shared_ptr
storage, and then the original is destroyed. std::make_shared<Class2>(Class2(3, 5))
创建一个临时Class2
,将其移到shared_ptr
存储中,然后将其销毁。
Your lambda captures an address of that temporary, and uses it after the temporary is destroyed. 您的lambda会捕获该临时地址,并在销毁该临时地址后使用它。
You need to avoid creating the temporary and construct Class2
directly in shared_ptr
storage: std::make_shared<Class2>(3, 5);
您需要避免创建临时类,并直接在
shared_ptr
存储中构造Class2
: std::make_shared<Class2>(3, 5);
. 。
Or even better, get rid of the lambda. 甚至更好,摆脱lambda。 I'm not sure why you want to use it there.
我不确定为什么要在那里使用它。
The compiler did what you told it to. 编译器按照您的指示进行了操作。
If so, why didn't the compiler reassign the proper value of this in the storage of m_func?
如果是这样,为什么编译器没有在m_func的存储中重新分配此值?
because you never told it to? 因为你从未告诉过?
[i, this](int x)
{
int val = *m_data_ptr;
return (val + m_data + i)*x;
};
the value of this
is captured the moment you create the lambda. 创建lambda时即会捕获
this
值。
No magic, other than dereferencing it when you use a member variable, happens. 除了使用成员变量时取消引用之外,没有任何魔术发生。
Then you copy m_func
from one object to another; 然后,将
m_func
从一个对象复制到另一个; the pointer in the lambda still points at the old object. Lambda中的指针仍然指向旧对象。 Then the old object is destroyed.
然后,旧物体被销毁。 Then you call
m_func
. 然后,您调用
m_func
。
Your copy constructor is broken. 您的副本构造函数已损坏。 Delete it:
删除它:
class Class1 {
Class1(Class1 const&)=delete;
because you store a pointer-to-yourself in the guts of m_func
and you never fix it while copying. 因为您将指向自己的指针存储在
m_func
并且在复制时从不对其进行修复。
This results in your code not compiling; 这导致您的代码无法编译; a fix makes it compile and removes the bug as well:
修复程序使其可以编译并删除该错误:
class2_ptr = std::make_shared<Class2>(3, 5);
the original created a temporary Class2
, then copied it into the shared ptr; Class2
创建了一个临时的Class2
,然后将其复制到共享的ptr中; this version creates one directly in place. 此版本直接创建一个。
Another approach is to fix the broken m_func
: 另一种方法是修复损坏的
m_func
:
std::function<int(Class1*, int)> m_func;
and 和
m_func = [i](Class1* self, int x)
{
int val = *self->m_data_ptr;
return (val + self->m_data + i)*x;
};
And where you use it: 以及在哪里使用它:
return m_class1obj.m_func(&m_class1obj, v);
and now you no longer have to =delete
Class1
's copy ctor, nor change how you construct it. 现在您不再需要
=delete
Class1
的副本ctor,也无需更改其构造方式。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.