Reputation: 816
In my current understanding, roughly speaking, (non-static
) local variables are destroyed when the thread of execution leaves their scope. However, I found that the exact points when the local variables are destroyed in different situations differ. I've been struggling with understanding how to determine when local variables are destroyed, and, in particular, the reason behind it. (possible related questions I asked include: 1, 2, 3)
Given 2 toy classes:
class A {
public:
A() { cout << "A created\n"; }
A(const A&) { cout << "A copied\n"; }
~A() { cout << "A destroyed" << endl; }
};
class B{
public:
B() { cout << "B created\n"; }
B(const B&) { cout << "B copied\n"; }
~B() { cout << "B destroyed\n"; }
};
B& f() {
A a;
static B b;
return b;
}
int main()
{
B b = f();
}
here the outputs are as follows:
A created
B created
A destroyed
B copied
B destroyed
B destroyed
In my current understanding, in example 1, the local (non-static
) variable(s) are destroyed immediately after the reference is returned, then the object being referenced is copied to b
.
B g() {
A a;
B b;
return b;
}
int main()
{
B b = g();
}
here the outputs are as follows:
A created
B created
B copied
B destroyed
A destroyed
B destroyed
this time, the copying to b
happens before the local variables are destroyed.
In my understanding, for C++17 and newer, what is returned to g()
is a prvalue and it is not an object. As a result, if the destruction of the local variables happens after the prvalue of g()
is created but before the copying (as in example 1), we would be copying memory that has been destroyed. So, I think of the outputs as "the compiler is waiting for the copying to be done before it destroys the local variable".
However, this is just a really vague understanding of mine. May I ask what actually happens behind the scenes so that such "waiting" can always happen properly?
void fun() {
A a;
B b;
throw b;
}
int main()
try
{
fun();
}
catch (B cb) { }
here the outputs are as follows:
A created
B created
B copied
B copied
B destroyed
A destroyed
B destroyed
B destroyed
In this situation, I think b
is used to copy initialize a temporary, this temporary is then used to copy initialize cb
. The local variables are destroyed after all those copying even though the temporary is an actually object, not prvalue. So, this situation is also different from example 1.
I am using Visual Studio 2022, C++20.
Upvotes: 1
Views: 927
Reputation: 30559
Function local variables are destroyed immediately after the function's return value is initialized.
In the first example, that return value is just a reference, so its creation has no observable side-effect. After the function returns, b
is then initialized using the returned reference, so you see the "B copied" output after f
has returned.
In the second example, the function's return value is a B
object. Specifically the variable b
in main
due to the way prvalues work. That means that b
is initialized directly by g
, so you see the "B copied" output before g
has returned.
In the third example, an exception is involved, and exceptions are complicated, and their specific mechanisms aren't terribly standardized. You can be sure that fun
's local variables will be destroyed before the catch
block is entered, but beyond that it's possible that the exception object could be copied any number of times during the stack unwinding.
Upvotes: 6