The following example compiles with both gcc 11 on Linux (GNU STL) and clang 12 on FreeBSD (Clang STL). On Linux, it runs and prints values 1 and 2. On FreeBSD, it prints value 1 and then crashes with a SEGV. I don't quite understand the object lifetimes -- so the whole thing may be UB and the runtime behavior might not be relevant. I do know that the implementation of std::unique_ptr between those two STLs differs in an important way: Clang STL resets the internal pointer of a std::unique_ptr to nullptr at the start of the destructor, while GNU STL leaves the pointer alone.
#include <iostream>
#include <memory>
struct C {
struct Private {
C* m_owner;
int m_x;
Private(C* owner) : m_owner(owner), m_x(0) {}
~Private() { m_owner->cleanup(); }
void cleanup() { std::cout << "Private x=" << ++m_x << '\n'; }
};
std::unique_ptr<Private> d;
C() { d = std::make_unique<Private>(this); }
~C() = default;
void cleanup() { d->cleanup(); }
};
int main(int argc, char **argv)
{
C c;
c.cleanup(); // For display purposes, print 1
return 0; // Destructors called, print 2
}
Output on FreeBSD:
Private x=1
Segmentation fault (core dumped)
and a snippet of backtrace:
* thread #1, name = 'a.out', stop reason = signal SIGSEGV: invalid address (fault address: 0x8)
frame #0: 0x00000000002032b4 a.out`C::Private::cleanup() + 52
a.out`C::Private::cleanup:
-> 0x2032b4 <+52>: movl 0x8(%rax), %esi
My reason for thinking this might be UB is this:
at return 0, c's lifetime is ending.
the destructor ~C() runs. Once the body (defaulted) of the destructor is done, the lifetime of the object is over and using that object is UB.
now the destructors for sub-objects (member-objects?) of the object run.
the destructor ~std::unique_ptr<Private> runs. It runs the destructor for the held object.
the destructor ~Private() uses a pointer to a no-longer-alive object m_owner to call a member function.
I'd appreciate an answer that points out if this understanding of object lifetimes is correct.
If it's not UB, then there's a separate quality-of-implementation issue (or I should check the d-pointer before calling methods on it, but that seems a bit derpy for a pimpl; then we get if(d)d->cleanup() which is needed with one STL implementation and which is a useless check in another).
In the interest of posing a single question: does this code exhibit UB in the statement m_owner->cleanup() (line 9) during the destruction of object c ?
Yes, the lifetime of the object that m_owner refers to has already ended and it's destructor call completed when m_owner->cleanup(); is called. The call is therefore UB.
Related
I was reading subsection 18.2 Constructors and Destructors of section 18 exception handling in C++17 standard draft where there is an example(page 384). I tried to understand where destruction of object A returned inside the try block happens but couldn't find. So I copied the example, added some prints and see that dtor of that object is never called. What am I missing here? Someone please explain what's happening here.
#include <stdio.h>
struct A {
int a_;
explicit A(int a):a_(a) {
printf("A(%d)'s ctor\n", a_);
}
~A() {
printf("A(%d)'s dtor\n", a_);
}
};
struct Y { ~Y() noexcept(false) {
printf("y's dtor\n");
throw 0; } };
A f() {
try {
A a(23);
Y y;
A b(56);
return A(100); // #1 who destructs this ??
} catch (...) {
printf("handling exception..\n");
}
printf("At line %d now..\n", __LINE__);
return A(200); // #2
}
int main() {
auto ret = f();
printf("f returned A(%d) object\n", ret.a_);
return 0;
}
Above code outputs following:
A(23)'s ctor
A(56)'s ctor
A(100)'s ctor
A(56)'s dtor
y's dtor
A(23)'s dtor
handling exception..
At line 34 now..
A(200)'s ctor
f returned A(200) object
A(200)'s dtor
...Program finished with exit code 0
According to the C++17 standard [except.ctor]/2:
If an exception is thrown during the destruction of temporaries or local variables for a return statement, the destructor for the returned object (if any) is also invoked. The objects are destroyed in the reverse order of the completion of their construction.
Then there is an illustrative example (which you modified slightly) to demonstrate: after the Y destructor throws, the next thing that happens is that the A(100) object should be destroyed, so you should see a destruction message.
The posted output indicates a compiler bug. This is very similar to the issue reported as LLVM bug 12286 and gcc bug 33799.
The latter is marked as being fixed in GCC 10 (after initially being reported in 2007!). However, testing with the version 10.0.1 20200418 (experimental) on Godbolt: even though the test case in the 33799 bug report is fixed, the code in this question remains unfixed. I added a comment to that bug report about this example.
Generally, the caller of a function is responsible for destroying the function's return value, but the precise details are part of the platform C++ ABI.
On most ABIs, calling a function that returns a non-trivial value (anything that doesn't safely fit in a register or perhaps two), is done by passing a hidden extra argument specifying where the return value should be constructed. The caller allocates space in it's frame for the return value and passes a pointer to that space.
I've always assumed that an object begins and ends its lifetime in the same memory location, but I've recently come across a scenario where I need to be sure. Specifically, I'm looking for a guarantee from the standard that no matter what optimizations the compiler performs the address an object is constructed at is the same one that it will have its destructor called from... and that its destructor is, indeed, guaranteed to be called from that location unless the program is terminating.
I've always taken this stuff for granted, but upon closer examination I can't find a guarantee, and there's some language around copy and move elision that I'm not sure how to interpret. I'm hoping that some of the more standards-conversant people here can point me to chapter and verse.
What you are looking for is defined in [intro.object]/1
[...] An object occupies a region of storage in its period of construction ([class.cdtor]), throughout its lifetime, and in its period of destruction ([class.cdtor]).
This means the address cannot change as long as you can access it.
Specifically, I'm looking for a guarantee from the standard that no matter what optimizations the compiler performs the address an object is constructed at is the same one that it will have its destructor called from...
and that its destructor is, indeed, guaranteed to be called from that location unless the program is terminating.
The standard guarantees both for automatic variables and static variables as long as one doesn't do bad things with the objects. However, it does not guarantee either for objects allocated from the free store.
Even for automatic variables, a crafty programmer can subvert the intention through pointer manipulation and explicitly calling the destructor through a pointer.
In addition, the wrong destructor will be called when delete-ing a base class pointer when the base class does not have a virtual destructor. This will be a programming error, not the result of intention to subvert.
Example:
struct Base
{
int b;
};
struct Derived : virtual Base
{
float d;
};
int main()
{
{
Derived d1; // Not a problem.
}
{
Derived d1;
Derived* ptr = &d1;
delete ptr; // Bad. The programmer subverts the program.
// Must not use delete.
}
{
Derived* d2 = new Derived; // The destructor does not get called automatically.
}
{
Derived* d2 = new Derived;
delete d2; // OK. The proper destructor gets called.
}
{
Derived* d2 = new Derived;
Base* ptr = d2;
delete ptr; // Programmer error. The wrong destructor gets called.
}
}
As mentioned by Nathan Oliver, the standard states that:
[...] An object occupies a region of storage in its period of construction ([class.cdtor]), throughout its lifetime, and in its period of destruction ([class.cdtor]).
Compilers respect this, and there are objects (similar to the one you describe) for which it must hold true. Consider std::mutex. A mutex cannot be copied or moved, and the reason for this is that it must remain at the same location in memory for the duration of it's lifetime in order to work.
So how does copy/move elision work?
Copy/move elision works by creating the object where it needs to go. It's that simple.
We can see this behavior for ourselves:
#include <iostream>
struct Foo {
Foo() {
std::cout << "I am at " << (void*)this << '\n';
}
// Delete copy and move, to ensure it cannot be moved
Foo(const Foo&) = delete;
Foo(Foo&&) = delete;
};
Foo getFoo() {
return Foo();
}
int main() {
Foo* ptr = new Foo(getFoo());
std::cout << "Foo ptr is at " << (void*)ptr << '\n';
delete ptr;
}
This code outputs:
I am at 0x201ee70
Foo ptr is at 0x201ee70
And we see that Foo remains at the same location for the duration of it's lifetime, without ever being copied or moved, even though it's being created in dynamically allocated memory.
How does the compiler know where to create an object?
If a function returns a type that is not trivially copyable, then that function takes an implicit parameter representing the memory address where it's supposed to construct the return value.
In jpeglib, one has to use setjmp/longjmp to implement custom error handling.
There are lots of resources where it is said that setjmp/longjmp do not play well with c++ (for example answers in this question tell they do go along with RAII), but the answers to this question say that the destructor is called.
I have this example (taken from here and modified a bit):
#include <iostream>
#include <csetjmp>
std::jmp_buf jump_buffer;
struct A
{
A(){std::cout<<"A()"<<std::endl;}
~A(){std::cout<<"~A()"<<std::endl;}
};
void a(int count)
{
std::cout << "a(" << count << ") called\n";
std::longjmp(jump_buffer, count+1); // setjump() will return count+1
}
int main()
{
// is this object safely destroyed?
A obj;
int count = setjmp(jump_buffer);
if (count != 9) {
a(count);
}
}
In this example, the destructor is called (as I expected), but is it the standard behaviour? Or is it compiler's extension, or simple UB?
The output:
A()
a(0) called
a(1) called
a(2) called
a(3) called
a(4) called
a(5) called
a(6) called
a(7) called
a(8) called
~A()
It can be undefined behaviour depending on whether or not destructors would be called were an exception to execute the same transfer of control. In C++03. From section 18.7 Other runtime support, paragraph 4:
The function signature longjmp(jmp_buf jbuf, int val) has more restricted behavior in this International Standard. If any automatic objects would be destroyed by a thrown exception transferring control to another (destination) point in the program, then a call to longjmp(jbuf, val) at the throw point that transfers control to the same (destination) point has undefined behavior.
There's similar language in c++11:
The function signature longjmp(jmp_buf jbuf, int val) has more restricted behavior in this International Standard. A setjmp/longjmp call pair has undefined behavior if replacing the setjmp and longjmp by catch and throw would invoke any non-trivial destructors for any automatic objects.
However, there appear to be no destructors that are called in transition for this particular piece of code, so I believe it's safe.
Now, if you were to change the code to move the creation to after the setjmp, that becomes undefined behaviour. In my setup (gcc 4.4.5 under Debian), the following code (with everything else identical to your question):
int main() {
int count = setjmp (jump_buffer);
A obj;
if (count != 4) a (count);
}
results in the output:
A()
a(0) called
A()
a(1) called
A()
a(2) called
A()
a(3) called
A()
~A()
and you can see the destructor is not being called as part of the jump although, being undefined, it may be on some systems.
The bottom line is, you shouldn't be jumping from region A to region B where the equivalent throw/catch would properly destruct an object, because there's no guarantee the longjmp will call the destructor.
Actually, there are some who would say you shouldn't use setjmp/longjmp in C++ at all, and I tend to lean that way myself. I have a hard time seeing a need for that even in C.
I think I used it once in my entire career (and that's a long career), to implement co-operative threading in Turbo C under MS-DOS. I can't think of one other time I've ever used it. Not to say there aren't any uses, but they'd be pretty rare.
Why is the destructor not invoked in this code?
#include <boost/scoped_ptr.hpp>
#include <iostream>
class MyClass {
boost::scoped_ptr<int> ptr;
public:
MyClass() : ptr(new int) { *ptr = 0; throw; std::cout<<"MyClass Allocated\n"; }
~MyClass() { std::cout<<"MyClass De-allocated\n"; }
int increment() { return ++*ptr; }
};
int main()
{
boost::scoped_ptr<MyClass> myinst(new MyClass);
std::cout << myinst->increment() << '\n';
std::cout << myinst->increment() << '\n';
}
EDIT
From the answers, In understand that when an exception happens in the constructor, destructor will not be invoked. But if the exception happens in the main(), ie after the MyClass object is fully instantiated, will the MyClass destructor be invoked? If not, then why it is a smart pointer?
Adding the code
#include <boost/scoped_ptr.hpp>
#include <iostream>
class MyClass {
boost::scoped_ptr<int> ptr;
public:
MyClass() : ptr(new int) { *ptr = 0; std::cout<<"MyClass Allocated\n"; }
~MyClass() { std::cout<<"MyClass De-allocated\n"; }
int increment() { return ++*ptr; }
};
int main()
{
boost::scoped_ptr<MyClass> myinst(new MyClass);
throw 3;
std::cout << myinst->increment() << '\n';
std::cout << myinst->increment() << '\n';
}
Output:
MyClass Allocated
terminate called after throwing an instance of 'int'
Aborted
A C++ object's lifetime begins only after its constructor completes successfully.
Since the exception was thrown before constructor call was complete you don't have an complete object and hence no destructor.
Herb Sutter explains this nicely, to quote him:
Q: What does emitting an exception from a constructor mean?
A: It means that construction has failed, the object never existed, its lifetime never began. Indeed, the only way to report the failure of construction -- that is, the inability to correctly build a functioning object of the given type -- is to throw an exception. (Yes, there is a now-obsolete programming convention that said, "if you get into trouble just set a status flag to 'bad' and let the caller check it via an IsOK() function." I'll comment on that presently.)
In biological terms,
conception took place -- the constructor began -- but despite best efforts it was followed by a miscarriage -- the constructor never ran to term(ination).
Incidentally, this is why a destructor will never be called if the constructor didn't succeed -- there's nothing to destroy. "It cannot die, for it never lived." Note that this makes the phrase "an object whose constructor threw an exception" really an oxymoron. Such a thing is even less than an ex-object... it never lived, never was, never breathed its first. It is a non-object.
We might summarize the C++ constructor model as follows:
Either:
(a) The constructor returns normally by reaching its end or a return statement, and the object exists.
Or:
(b) The constructor exits by emitting an exception, and the object not only does not now exist, but never existed as an object.
EDIT 1:
But if the exception happens in the main(), ie after the MyClass object is fully instantiated, will the MyClass destructor be invoked?
Yes, it will be!
That is the purpose of using scoped_ptr, Once an exception is thrown in main, Stack Unwinding would cause all local objects to be deallocated, this means that myinst(which resides on stack) will also be deallocated, which in turn will call the destructor of MyClass.
Refer the Boost doccumentation when in doubt:
The scoped_ptr class template stores a pointer to a dynamically allocated object. (Dynamically allocated objects are allocated with the C++ new expression.) The object pointed to is guaranteed to be deleted, either on destruction of the scoped_ptr, or via an explicit reset
EDIT 2:
Why does your edited program crash?
Your program shows crashes because, You throw an exception but you never catch it. when such a scenario occurs an special function called terminate() is called whose default behavior is to call abort().It is implementation defined behavior whether stack is Unwound before terminate() is called in this particular scenarioRef 1.Seems your implementation doesn't & you should not rely on this behavior as well.
You can modify your program as follows to handle the exception and you should get the behavior you were expecting:
#include <boost/scoped_ptr.hpp>
#include <iostream>
class MyClass {
boost::scoped_ptr<int> ptr;
public:
MyClass() : ptr(new int) { *ptr = 0; std::cout<<"MyClass Allocated\n"; }
~MyClass() { std::cout<<"MyClass De-allocated\n"; }
int increment() { return ++*ptr; }
};
void doSomething()
{
boost::scoped_ptr<MyClass> myinst(new MyClass);
throw 3;
}
int main()
{
try
{
doSomething();
}
catch(int &obj)
{
std::cout<<"Exception Handled";
}
}
Ref1C++03 15.5.1 The terminate() function
In the following situations exception handling must be abandoned for less subtle error handling techniques:
....
— when the exception handling mechanism cannot find a handler for a thrown exception (15.3),
....
In such cases,
void terminate();
is called (18.6.3). In the situation where no matching handler is found, it is implementation-defined whether or not the stack is unwound before terminate() is called. In all other situations, the stack shall not be unwound before terminate() is called. An implementation is not permitted to finish stack unwinding prematurely based on a determination that the unwind process will eventually cause a call to terminate().
Because calling the destructor doesn't make sense in this case.
You only destruct things which are constructed, yet your object never fully constructs. Your class members have been constructed, though, and will have their destructors called.
If a constructor throws exception, then the destructor of the class will not be called, because the object is not fully constructed.
See this link how to manage resources in such situation:
http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.10
When the exception is thrown from the constructor (beginning or half way or at the end of the call), then it's assured that the object is not constructed.
So it's well defined not to invoke the destructor of an object which was never constructed.
Here is one related FAQ from Bjarne's website.
The destructor for MyClass was never invoked because no objects of type MyClass were ever constructed. Each attempt to construct one was aborted, due to the exception being thrown.
As an aside, if you want your debug messages to display -- especially if you're dealing with the program crashing -- you really ought to flush the streams: i.e. using std::endl instead of '\n' at the end of line. (or inserting std::flush)
While merely using '\n' often works, there are enough situations where it fails and it's really, really confusing to debug if you don't make a habit of doing things right.
Suppose I have a pointer to a dynamically allocated array of 10 elements:
T* p = new T[10];
Later, I want to release that array:
delete[] p;
What happens if one of the T destructors throws an exception? Do the other elements still get destructed? Will the memory be released? Will the exception be propagated, or will program execution be terminated?
Similarly, what happens when a std::vector<T> is destroyed and one of the T destructors throws?
I can not see it explicitly called out in the standard:
Just that they will be called in reverse order of creation
5.3.5 Delete [expr.delete]
6 If the value of the operand of the delete-expression is not a null pointer value, the delete-expression will invoke the destructor (if any) for the object or the elements of the array being deleted. In the case of an array, the elements will be destroyed in order of decreasing address (that is, in reverse order of the completion of their constructor; see 12.6.2).
And that the memory deallocation will be done even if the exception is thrown:
7 If the value of the operand of the delete-expression is not a null pointer value, the delete-expression will call a deallocation function (3.7.4.2). Otherwise, it is unspecified whether the deallocation function will be called. [ Note: The deallocation function is called regardless of whether the destructor for the object or some element of the array throws an exception. — end note ]
I tried the following code in G++ and it shows that that no more destructors get called after the exception:
#include <iostream>
int id = 0;
class X
{
public:
X() { me = id++; std::cout << "C: Start" << me << "\n";}
~X() { std::cout << "C: Done " << me << "\n";
if (me == 5) {throw int(1);}
}
private:
int me;
};
int main()
{
try
{
X data[10];
}
catch(...)
{
std::cout << "Finished\n";
}
}
Execute:
> g++ de.cpp
> ./a.out
C: Start0
C: Start1
C: Start2
C: Start3
C: Start4
C: Start5
C: Start6
C: Start7
C: Start8
C: Start9
C: Done 9
C: Done 8
C: Done 7
C: Done 6
C: Done 5
Finished
Which all leads back to this (very old answer):
throwing exceptions out of a destructor
5.3.5.7 If the value of the operand of the
delete-expression is not a null
pointer value, the delete-expression
will call a deallocation function
(3.7.3.2). Otherwise, it is unspecified
whether the deallocation function will
be called. [ Note: The deallocation
function is called regardless of
whether the destructor for the object
or some element of the array throws an
exception. — end note ]
Couldn't find anything about destructors except for
In the case of an array, the elements will be
destroyed in order of decreasing address (that is, in reverse order of the completion of their constructor; see 12.6.2).
I guess that after throwing no more destructors are called, but I'm not sure.
Never do that. If there is already an active exception, std::terminate will be called: "Bang, you're dead". Your destructor must. Not. Throw. Resist.
edit: Relevant section from the Standard (14882 2003), 15.2 Constructors and Destructors [except.dtor] :
15.2.3 The process of calling destructors for automatic objects constructed on the path from a try block to a
throw-expression is called “stack unwinding.” [Note: If a destructor called during stack unwinding exits with an exception, terminate is called (15.5.1). So destructors should generally catch exceptions and
not let them propagate out of the destructor. —end note]
Testcase for playing around (in real life, throw something that is derived from std::exception, never throw int or something else!):
#include <iostream>
int main() {
struct Foo {
~Foo() {
throw 0; // ... fore, std::terminate is called.
}
};
try {
Foo f;
throw 0; // First one, will be the active exception once Foo::~Foo()
// is executed, there- ...
} catch (int) {
std::cout << "caught something" << std::endl;
}
}
To answer your second question, if you used std::vector instead, there wouldn't be any need for a call to delete, you're not using pointers (the vector class is internally I believe, but this is not up to you to manage).
Okay, here is some experimental code:
#include <cstddef>
#include <cstdlib>
#include <new>
#include <iostream>
void* operator new[](size_t size) throw (std::bad_alloc)
{
std::cout << "allocating " << size << " bytes" << std::endl;
return malloc(size);
}
void operator delete[](void* payload) throw ()
{
std::cout << "releasing memory at " << payload << std::endl;
free(payload);
}
struct X
{
bool throw_during_destruction;
~X()
{
std::cout << "destructing " << (void*)this << std::endl;
if (throw_during_destruction) throw 42;
}
};
int main()
{
X* p = new X[10]();
p[5].throw_during_destruction = true;
p[1].throw_during_destruction = true;
delete[] p;
}
Running the code gave the following output on g++ 4.6.0:
allocating 14 bytes
destructing 0x3e2475
destructing 0x3e2474
destructing 0x3e2473
destructing 0x3e2472
destructing 0x3e2471
terminate called after throwing an instance of 'int'
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
So it would seem that std::terminate is called immediately as soon as the first destructor throws. The other elements are not destructed, and the memory is not released. Can anyone confirm this?
If an exception is thrown, it is thrown. The object that failed to destruct is obviously not properly destroyed, and neither are the ones remaining in the array.
If you use a vector, the problem is the same, just not in your code. :-)
So, throwing destructors is just a Bad Idea(tm).
Like #Martin shows below, the object that did thrown is formally non-existent as soon as we enter the destructor. The others might have their memory reclaimed as well.
However, it obviously contained some complicated things that were not properly of flusher flushed. If that object, and the others following it in the array, contained some mutex locks, open files, database caches, or shared_ptrs, and none of those had their destructors run, we are likely in BIG trouble.
Having std::terminate called at that point, to put the program out of its misery, seems like something you would wish for!