I'm having a problem with making one trivial C++ project involving lambda-expressions and shared pointers to work. The project is in Visual Studio, Debug, x64. This is the code I have.
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;
}
The code compiles fine, but crashes while executing the last line of main()
. While debugging, I found that the problem discovers itself upon completion of the line 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! 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;
, thus causing the crash, even though I assigned a non-null address in class2_ptr->Assign(val);
prior to calling m_func
! But why did the address of this
change? 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
? 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
.
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! 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? I can't seem to find the explanation in the C++ standard for my situation.
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.
Your lambda captures an address of that temporary, and uses it after the temporary is destroyed.
You need to avoid creating the temporary and construct Class2
directly in shared_ptr
storage: std::make_shared<Class2>(3, 5);
.
Or even better, get rid of the lambda. I'm not sure why you want to use it there.