if I have something along the lines of:
auto foo=Foo(Bar())
where Foo's constructor takes a const reference to Bar, is there some way to ensure that Bar's destructor will not get called before the destructor on foo at the end of the scope of foo, so that the reference to Bar will still be valid even in foo's destructor?
The relative order of the destructors is guaranteed.
auto foo=Foo(Bar());
Since Bar() is a temporary, then it is guaranteed to be destructed at the end of the expression: aka the semicolon. You are absolutely guaranteed that the Bar is destroyed before foo.
There is no way to extend the lifetime of this temporary, you'll have to make a copy of it (potentially by moving it). Alternatively:
{
Bar b;
auto foo=Foo(b);
}
Since the objects in a single scope are destroyed in the opposite order they're constructed, then since these are in the same scope, b will be destroyed after foo is destroyed.
Related
I have the following code:
#include <stdio.h>
class Foo {
public:
int a;
~Foo() { printf("Goodbye %d\n", a); }
};
Foo newObj() {
Foo obj;
return obj;
}
int main() {
Foo bar = newObj();
bar.a = 5;
bar = newObj();
}
When I compile with g++ and run it, I get:
Goodbye 32765
Goodbye 32765
The number printed seems to be random.
I have two questions:
Why is the destructor called twice?
Why isn't 5 printed the first time?
I'm coming from a C background, hence the printf, and I'm having trouble understanding destructors, when they are called and how a class should be returned from a function.
Let's see what happens in your main function :
int main() {
Foo bar = newObj();
Here we just instantiate a Foo and initialize it with the return value of newObj(). No destructor is called here because of copy elision: to sum up very quickly, instead of copying/moving obj into bar and then destructing obj, obj is directly constructed in bar's storage.
bar.a = 5;
Nothing to say here. We just change bar.a's value to 5.
bar = newObj();
Here bar is copy-assigned1 the returned value of newObj(), then the temporary object created by this function call is destructed2, this is the first Goodbye. At this point bar.a is no longer 5 but whatever was in the temporary object's a.
}
End of main(), local variables are destructed, including bar, this is the second Goodbye, which does not print 5 because of previous assignment.
1 No move assignment happens here because of the user-defined destructor, no move assignment operator is implicitly declared.
2 As mentioned by YSC in the comments, note that this destructor call has undefined behavior, because it is accessing a which is uninitialized at this point. The assignment of bar with the temporary object, and particularly the assignment of a as part of it, also has undefined behavior for the same reasons.
1) It's simple, there are two Foo objects in your code (in main and in newObj) so two destructor calls. Actually this is the minimum number of destructor calls you would see, the compiler might create an unnamed temporary object for the return value, and if it had done that you would see three destructor calls. The rules on return value optimization have changed over the history of C++ so you may or may not see this behaviour.
2) Because the value of Foo::a is never 5 when the destructor is called, its never 5 in newObj, and though it was 5 in mainit isn't by the time you get to the end of main (which is when the destructor is called).
I'm guessing your misunderstanding is that you think that the assignment statement bar = newObj(); should call the destructor, but that's not the case. During assignment an object gets overwritten, it doesn't get destroyed.
I think one of the main confusions here is object identity.
bar is always the same object. When you assign a different object to bar, you don't destroy the first one - you call operator=(const& Foo) (the copy assignment operator). It is one of the five special member functions that can be auto-generated by the compiler (which it is in this case) and just overwrites bar.a with whatever is in newObj().a. Provide your own operator= to see that/when this happens (and to confirm that a is indeed 5 before this happens).
bar's destructor is only called once - when bar goes out of scope at the end of the function. There is only one other destructor call - for the temporary returned by the second newObj(). The first temporary from newObj() is elided (the language allows it in this exact case because there is never really a point to creating and immediately destroying it) and initializes bar directly with the return value of newObj().
Imagine a class like this:
class foo {
public:
foo() : _bar{new bar}, _baz{new baz} {}
private:
unique_ptr<bar> _bar;
unique_ptr<baz> _baz;
};
So whenever an instance of foo gets destructed, then in what order will the data members of it be destroyed, if that's defined behavior at all?
Let's say that _baz does depend on the existance of _bar, maybe it uses some resource that _bar owns (let's assume it knows about the _bar object, even though the constructor doesn't reflect this).
So in this case, if _bar gets destructed first (when its time to destruct foo), then _baz could potentially try to access some resource which has been freed by _bar's destructor.
An obvious solution is to implement a destructor in foo, which manually frees _baz and _bar in the right order, but what if there's no destructor implemented? Is there a default behavior that defines the destruction order of data members?
The order of destruction of data members is the reverse of their order of declaration, the same way as variables declared within a scope:
{
// a, b constructed in that order
bar a;
baz b;
} // b, a destroyed in that order
it is stated on this website: http://www.tutorialspoint.com/cplusplus/cpp_variable_scope.htm
Variables that are declared inside a function or block are local
variables. They can be used only by statements that are inside that
function or block of code. Local variables are not known to functions
outside their own.
Then, in the following example;
class foo {
/*....*/
};
foo bar(){
foo f;
return f;
}
void main(){
foo fooReturn = bar();
}
how come when bar() returns, fooReturn contains a valid object? is:
foo f similar to foo *f = new foo(); are both objects on the heap?
thanks
daniel
No, foo f; is very different from foo * f = new foo();, since the former foo is built on the stack, its destructor is automatically called when it goes out of scope, etc.
Instead, the latter foo is built on the heap, requires manual destruction calling delete, etc.
But, in your sample code, the returned foo f is copied or moved (if foo provides move semantics, e.g. move constructor), out of the function bar(). So you have a valid object returned to the caller.
Note
The be more precise, there is an optimization that the C++ compiler may apply, i.e. the RVO (Return Value Optimization), that could avoid the copy or move of the returned foo.
how come when bar() returns, fooReturn contains a valid object?
Because the value of the return expression (f) is used to initialise fooReturn before it's destroyed. As long as the type has correct copy/move semantics, or the copy/move is elided, the resulting object will be valid.
is: foo f similar to foo *f = new foo(); are both objects on the heap?
No, the first is an automatic variable, stored in the function's stack frame and destroyed when it goes out of scope. The second is a dynamic object, stored on the heap, and not destroyed without an explicit delete.
When you return a local object from a function, a copy is created (with a copy costructor). In your example, fooReturn contains a copy of the f object (local to bar). After the f is copied, it is freed.
Consider the following piece of code:
struct foo {
std::string id;
};
int main() {
std::vector<foo> v;
{
foo tmp;
v.push_back(std::move(tmp));
}
}
LIVE DEMO
In the piece of code demonstrated:
The default constructor of class foo is going to be invoked for the construction of object tmp.
The move constructor of class foo is going to be invoked in the statement v.push_back(std::move(tmp));.
The destructor of class foo is going to be invoked twice.
Questions:
Why the destructor of a moved from object is called twice?
What is moved from the object that is being moved really?
Why the destructor of a moved object is called twice?
The first destructor destroys the moved-from tmp when it goes out of scope at the first } in main(). The second destructor destroys the move-constructed foo that you push_back'd into v at the end of main() when v goes out of scope.
What is moved from the object that is being moved really?
The compiler-generated move constructor move-constructs id, which is a std::string. A move constructor of std::string typically takes ownership of the block of memory in the moved-from object storing the actual string, and sets the moved-from object to a valid but unspecified state (in practice, likely an empty string).
In GMan's answer here, the destructor of the restore_base class isn't virtual, so I keep wondering how exactly that works. Normally you'd expect the destructor of restorer_base to be executed only, after the object goes out of scope, but it seems that the derived restorer_holder destructor is really called. Anyone care to enlighten me?
The standard case where you need a virtual destructor is
void foo()
{
scoped_ptr<Base> obj = factory_returns_a_Derived();
// ... use 'obj' here ...
}
And the standard case where you don't is
void foo()
{
Derived obj;
// ... use 'obj' here ...
}
GMan's code is doing something a little trickier, that turns out to be equivalent to the second case:
void foo()
{
Base& obj = Derived();
// ... use 'obj' here ...
}
obj is a bare reference; normally, it would not trigger destructors at all. But it's initialized from an anonymous temporary object whose static type -- known to the compiler -- is Derived. When that object's lifetime ends, the compiler will call the Derived destructor. Normally an anonymous temporary object dies at the end of the expression that created it, but there's a special case for temporaries initializing a reference: they live till the reference itself dies, which here is the end of the scope. So you get pseudo-scoped_ptr behavior and you don't need a virtual destructor.
EDIT: Since this has now come up twice: The reference does not have to be const for this special rule to apply. C+98 [class.temporary]/5:
The second context [in which a temporary object is not destroyed at the end of the
full-expression] is when a reference is bound to a temporary. The temporary to which
the reference is bound or the temporary that is the complete object to a subobject of
which the temporary is bound persists for the lifetime of the reference ...
Emphasis mine. There is no mention of const in this language, so the reference does not have to be const.
EDIT 2: Other rules in the standard prohibit creation of non-const references to temporary objects that are not lvalues. I suspect that at least some temporary objects are lvalues, but I don't know for certain. Regardless, that does not affect this rule. It would still be formally true that non-const references to temporary objects prolong their lifetime even if no strictly conforming C++ program could ever create such a reference. This might seem ridiculous, but you're supposed to read standardese this literally and pedantically. Every word counts, every word that isn't there counts.