I have class A
and a main()
function:
class A {
public: int num;
public: A* parent;
A(){};
A::A (const A &s)
{
this->num = s.num;
this->parent = s.parent;
}
public : vector <A> foo(A a)
{
A a1;
a1.num = a.num;
a1.parent = &a;
vector <A> list;
list.reserve(1);
list.push_back(a1);
A temp = list.front();
cout << temp.parent->num << endl;
return list;
}
};
int main()
{
A a;
a.num =2;
vector <A> list = a.foo(a);
A temp = list.front();
cout << temp.parent->num << endl;
return 0;
}
The problem is that cout
inside foo()
prints 2 as expected, but cout
inside main()
is printing a huge number.
I assume the problem is something related to memory rellocation, as I read from multiple sources, so I tried using reserve()
but it didn't work.
Can anyone help me solve this problem?
CodePudding user response:
In foo()
, you are passing in the a
parameter by value. That means the A
object that main()
is passing to foo()
gets copied into the a
parameter. The a
parameter itself is a local variable to foo()
. As such, a1.parent
is being set to point at a local variable.
And then list.push_back(a1);
makes a copy of a1
, which copies the parent
pointer. And then you are making another copy when saving the result of list.front()
to temp
.
Consequently, dereferencing the temp.parent
pointer inside of foo()
is perfectly valid, since a
that parent
is pointing at is still valid in memory.
However, once foo()
exits, a
gets destroyed, leaving the copied parent
pointer(s) dangling, pointing at now-invalid memory. As such, in main()
, dereferencing the parent
pointer is undefined behavior since that pointer is no longer pointing at the original a
object.
You can easily see this for yourself if you add some logging for the construction and destruction of your A
objects, eg:
class A {
public:
int num = 0;
A* parent = nullptr;
A(){ cout << "A created: " << this << endl; }
A(const A &s) : num(s.num), parent(s.parent)
{
cout << "A copied: " << &s << " -> " << this << endl;
}
~A(){ cout << "A destroyed: " << this << endl; }
...
};
You will see something like this being logged:
A created: 0x7ffe5e735250 // 'a' in main() is created
A copied: 0x7ffe5e735250 -> 0x7ffe5e735260 // 'a' in foo() is passed in by main()
A created: 0x7ffe5e7351e0 // 'a1' in foo() is created
A copied: 0x7ffe5e7351e0 -> 0x5592f50b2e80 // 'a1' is copied into `list` in foo()
A copied: 0x5592f50b2e80 -> 0x7ffe5e7351f0 // list.front() is copied into 'temp' in foo()
(foo) temp.parent: 0x7ffe5e735260, num: 2 // parent is pointing at 'a' in foo()
// foo() exits, 'list' is transferred to main()
A destroyed: 0x7ffe5e7351f0 // 'temp' in foo() is destroyed
A destroyed: 0x7ffe5e7351e0 // 'a1' in foo() is destroyed
A destroyed: 0x7ffe5e735260 // 'a' in foo() is destroyed !!!!!
A copied: 0x5592f50b2e80 -> 0x7ffe5e735260 // main() reuses freed memory for 'temp' in main() !!!!!
(main) temp.parent: 0x7ffe5e735260, num: 2 // parent is pointing at the wrong A object !!!!!
// main exits
A destroyed: 0x7ffe5e735260 // 'temp' in main() is destroyed
A destroyed: 0x5592f50b2e80 // `list` in main() is destroyed
A destroyed: 0x7ffe5e735250 // 'a' in main() is destroyed
To solve this issue, change foo()
to take in the A
object by reference instead (and also change the two temp
variables to be A&
references, as well):
vector<A> foo(A &a)
This way, in main()
, the returned parent
pointer will remain valid as long as the local a
object that is being passed into foo()
remains valid.
Now, you will see something like this being logged instead:
A created: 0x7ffd7319e8b0 // 'a' in main() is created
// 'a' in foo() is passed in by main()
A created: 0x7ffd7319e850 // 'a1' in `foo() is created
A copied: 0x7ffd7319e850 -> 0x556ccff0fe80 // 'a1' is copied into 'list' in foo()
(foo) temp.parent: 0x7ffd7319e8b0, num: 2 // parent is pointing at 'a' in main()
// foo() exits, 'list' is transferred to main()
A destroyed: 0x7ffd7319e850 // 'a1' in foo() is destroyed
(main) temp.parent: 0x7ffd7319e8b0, num: 2 // parent is still pointing at 'a' in main()
// main() exits
A destroyed: 0x556ccff0fe80 // 'list' in main() is destroyed.
A destroyed: 0x7ffd7319e8b0 // 'a' in main() is destroyed !!!!!