Is a C++ destructor guaranteed not to be called until the end of the block?
In the C++ code below, am I guaranteed that the ~obj() destructor will be called after the // More code executes? Or is the compiler allowed to destruct the obj object earlier if it detects that it's not used?
{
SomeObject obj;
... // More code
}
I'd like to use this technique to save me having to remember to reset a flag at the开发者_Go百科 end of the block, but I need the flag to remain set for the whole block.
You are OK with this - it's a very commonly used pattern in C++ programming. From the C++ Standard section 12.4/10, referring to when a destructor is called:
for a constructed object with automatic storage duration when the block in which the object is created exits
Actually...
C++ has something called the "as if" principle. All the guarentees made referenced in all of these answers only refer to the observable behavior. The compiler is allowed to ellude, reorder, add, etc.. any function call, as long as the observable behavior is as if it had executed as originally written. This also applies to destructors.
So, technically, your observation is correct: the compiler is allowed to destruct the object earlier, if it detects it is not used, and there are no observable side effects from the destructor or any function it calls. But, you are guarenteed to not be able to tell this is happening outside of a debugger, because if you were able to tell, the compiler would no longer be able to do it.
It's more likely the compiler uses this power to do something useful like completely ellude a trivial destructor rather than actually reorder destructor calls, however.
Edit: Someone wanted a reference... 1.9/5, along with footnote 4 of the C++0x draft standard (this isn't a new rule, I just don't have the C++03 standard handy. It's also present in the C standard, AFAIK)
1.9/5:
A conforming implementation executing a well-formed program shall produce the same observable behavior as one of the possible execution sequences of the corresponding instance of the abstract machine with the same program and the same input. However, if any such execution sequence contains an undefined operation, this International Standard places no requirement on the implementation executing that program with that input (not even with regard to operations preceding the first undefined operation).
Footnote 4:
This provision is sometimes called the “as-if” rule, because an implementation is free to disregard any requirement of this International Standard as long as the result is as if the requirement had been obeyed, as far as can be determined from the observable behavior of the program. For instance, an actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no side effects affecting the observable behavior of the program are produced.
My reading (and what I thought was the general understanding) was that this is what enables the compiler free hand to do whatever it wants (ie, enables optimizations), as long as the observable behavior is that of the original written source - including moving around destructors, not destructing objects at all, inventing destructors, etc.
The destructor will not be called until the object goes out of scope.
The C++ faq lite has a good section on dtors
Destruction in C++ is deterministic - meaning that the compiler is not free to move that code around. (Of course optimization might inline the destructor, determine that the destructor code does not interact with // More code
and do some instruction reordering, but that's another issue)
If you couldn't depend on the destructors being called when they are supposed to be called, you couldn't use RAII to grab locks (or just about any other RAII construct for that matter):
{
LockClass lock(lockData);
// More code
} // Lock automatically released.
Also, you can depend on destructors running in reverse order of how the objects were constructed.
Yes, it is guaranteed.
The lifetime of an object with automatic storage duration ends at the end of its potential scope and not before. For such an object the potential scope begins at the point of declaration and ends at the end of the block in which it is declared. This is the moment when the destructor will be called.
Note, that very pedantically speaking, even for an automatic object it is not correct to say it is destroyed when it "goes out of scope" (as opposed to "goes out of its potential scope"). The object can go out of scope and back into scope many times (if even more local objects with the same name are declared within the block), and going out of scope in such fashion does not cause the destruction of the object. It is the "very final end" of its scope that kills the automatic object, which is defined as the end of its potential scope as described above.
In fact, the language standard does not even rely on the notion of scope to describe the lifetime of automatic objects (no need to deal with all these terminological intricacies). It just says that the object is destroyed at the exit of the block in which it is defined :)
All of the answers here address what happens with named objects, but for completeness, you probably should know the rule for temporary/anonymous objects too. (e.g. f(SomeObjectConstructor()
or f(someFunctionThatReturnsAnObject())
)
Temporary objects are destroyed as the last step in evaluating the full-expression (1.9) that (lexically) contains the point where they were created. This is true even if that evaluation ends in throwing an exception. (12.2/3 from the ISO C++98 standard)
which basically means that temporarily generated objects persist until the next statement. Two exceptions are for temporaries generated as part of an object's initialization list (in which case the temporary is destroyed only after the object is fully constructed) and if a reference is made to a temporary (e.g. const Foo& ref = someFunctionThatReturnsAnobject()
) (which case the lifetime of the object is the lifetime of the reference).
Yes, the C++ standard has very specific requirements about when objects are destroyed (in §12.4/10), and in this case it must not be destroyed until after all the other code in the block has finished executing.
A typical example of this, just as your question is the boost::scoped_ptr (or similar std::auto_ptr) :
{
boost::scoped_ptr< MyClass > pMyClass( new MyClass );
// code using pMyClass here
} // destruction of MyClass and memory freed
精彩评论