Validity of "this" in destructor - c++

On the last line of a destructor, I have a diagnostic type message which takes a printf-like form:
"object destroyed at %p", this
I have concerns though about how well this is defined at such a point.
Should I have such reservations? Is the behaviour well-defined?

According to the C++ Standard (12.4 Destructors)
8 After executing the body of the destructor and destroying any
automatic objects allocated within the body, a destructor for class
X calls the destructors for X’s direct non-variant non-static data
members, the destructors for X’s direct base classes and, if X is the
type of the most derived class (12.6.2), its destructor calls the
destructors for X’s virtual base classes.
So your code is well-formed. All destructors of non-static data members and base classes are called after executing the body of the destructor.

Well, the pointer itself certainly still exists (it's just an address, after all). There should be no problem to print the pointer value.
On the other hand, everything that you did in the destructor has already happened. Attributes may already have been delete'd, etc, so you have to avoid anything that accesses those.

This has perfectly well defined behaviour. Consider that the this pointer can be used implicitly or explicitly throughout the destructor, e.g. whenever you access a member variable for things like delete ptr_;. After the destructor returns, the members are destroyed in reverse order of declaration/creation then the base destructors invoked.

As you might now, you can access your class members from the destructor. This would not be working if the this pointer was invalid. So you can safely assume that the address this points to is still the same that you might have printed in the constructor.

Inside the destructor the this pointer is well defined, as are all the members and bases (that will be destroyed in construction reverse order after the destructor return). So printing the address it refers is not UB.
The only thing is that the object itself cannot be assumed anymore as "polymorphic", since the derived components had already been destroyed.
class A
{
public:
virtual void fn() { std::cout << "A::fn" << std::endl; }
virtual ~A() { fn(); } //< will call A::fn(), even if B is destroying
};
class B: public A
{
public:
virtual void fn() { std::cout << "B::fn" << std::endl; }
virtual ~B() {}
};
int main()
{
B b;
A& a = b;
a.fn(); //< will print B::fn(), being A::fn virtual and being B the runtime-type of the a's referred object
return 0; //< will print A::fn() from b's A's component destructor
}

Related

What is allowed to do in destructors in C++?

I'm trying to understand what is allowed to do in destructors.
The standard says: "For an object with a non-trivial destructor, referring to any non-static member or base class of the object after the destructor finishes execution results in undefined behavior".
cppreference describes destruction sequence this way: "For both user-defined or implicitly-defined destructors, after the body of the destructor is executed, the compiler calls the destructors for all non-static non-variant members of the class".
Does this mean, that in the following code calling method from its member's destructor is UB? Or by "referring" standard means something particular?
struct Foo {
Foo(Callback cb) : cb_(cb) {}
~Foo() {
// body of Bar destructor finished at this moment;
// cb_() calls Bar::call_me()
cb_();
}
Callback cb_;
};
struct Bar {
// pass callback with captured this
Bar() : foo_([this]() { call_me(); }) {
}
void call_me() {
}
// foo is a member, its destructor will be called after Bar destructor
Foo foo_;
};
Also, what does the phrase "after the destructor finishes" from the standard mean exactly? After the body of a destructor finishes? Or after all members and base classes destroyed?
I think the answer to the last question is the key to understanding what is allowed and what is not.
The destructor of Bar has not finished, and therefore referring to a member of Bar, and indeed calling a member function of Bar within its destructor is OK.
Calling member functions of the super object can be a bit precarious though, since member functions may access sub objects, and some sub objects may have already been destroyed by the time the member function is called, and in that case accessing the destroyed objects would result in undefined behaviour. This is not the case in your example.
Or by "referring" standard means something particular?
I think it means to form a pointer or a reference to a sub object. As is done in the example below the rule.
Also, what does the phrase "after the destructor finishes" from the standard mean exactly? After the body of a destructor finishes? Or after all members and base classes destroyed?
The latter.
The body is executed first, then the destructor calls the sub object destructors, and then the destructor has finished.

Accessing owner in destructor c++

Say there is an object A which owns an object B via std::unique_ptr<B>. Further B holds a raw pointer(weak) reference to A. Then the destructor of A will invoke the destructor of B, since it owns it.
What will be a safe way to access A in the destructor of B? (since we may also be in the destructor of A).
A safe way me be to explicitly reset the strong reference to B in the destructor of A, so that B is destroyed in a predictable manner, but what's the general best practice?
I'm no language lawyer but I think it is OK. You are treading on dangerous ground and perhaps should rethink your design but if you are careful I think you can just rely on the fact that members are destructed in the reverse order they were declared.
So this is OK
#include <iostream>
struct Noisy {
int i;
~Noisy() { std::cout << "Noisy " << i << " dies!" << "\n"; }
};
struct A;
struct B {
A* parent;
~B();
B(A& a) : parent(&a) {}
};
struct A {
Noisy n1 = {1};
B b;
Noisy n2 = {2};
A() : b(*this) {}
};
B::~B() { std::cout << "B dies. parent->n1.i=" << parent->n1.i << "\n"; }
int main() {
A a;
}
Live demo.
since the members of A are destructed in order n2 then b then n1. But this is not OK
#include <iostream>
struct Noisy {
int i;
~Noisy() { std::cout << "Noisy " << i << " dies!" << "\n"; }
};
struct A;
struct B {
A* parent;
~B();
B(A& a) : parent(&a) {}
};
struct A {
Noisy n1 = {1};
B b;
Noisy n2 = {2};
A() : b(*this) {}
};
B::~B() { std::cout << "B dies. parent->n2.i=" << parent->n2.i << "\n"; }
int main() {
A a;
}
Live demo.
since n2 has already been destroyed by the time B tries to use it.
What will be a safe way to access A in the destructor of B? (since we may also be in the destructor of A).
There isn't safe way:
3.8/1
[...]The lifetime of an object of type T ends when:
— if T is a class type with a non-trivial destructor (12.4), the destructor call starts [...]
I think it's straightforward that you can't access object after it's lifetime has ended.
EDIT: As Chris Drew wrote in comment you can use object after it's destructor started, sorry, my mistake I missed out one important sentence in the standard:
3.8/5
Before the lifetime of an object has started but after the storage which the object will occupy has been
allocated or, after the lifetime of an object has ended and before the storage which the object occupied is
reused or released, any pointer that refers to the storage location where the object will be or was located
may be used but only in limited ways. For an object under construction or destruction, see 12.7. Otherwise,
such a pointer refers to allocated storage (3.7.4.2), and using the pointer as if the pointer were of type void*,
is well-defined. Such a pointer may be dereferenced but the resulting lvalue may only be used in limited
ways, as described below. The program has undefined behavior if:
[...]
In 12.7 there is list of things you can do during construction and destruction, some of the most important:
12.7/3:
To explicitly or implicitly convert a pointer (a glvalue) referring to an object of class X to a pointer (reference)
to a direct or indirect base class B of X, the construction of X and the construction of all of its direct or
indirect bases that directly or indirectly derive from B shall have started and the destruction of these classes shall not have completed, otherwise the conversion results in undefined behavior. To form a pointer to (or
access the value of) a direct non-static member of an object obj, the construction of obj shall have started
and its destruction shall not have completed, otherwise the computation of the pointer value (or accessing
the member value) results in undefined behavior.
12.7/4
Member functions, including virtual functions (10.3), can be called during construction or destruction (12.6.2).
When a virtual function is called directly or indirectly from a constructor or from a destructor, including
during the construction or destruction of the class’s non-static data members, and the object to which the
call applies is the object (call it x) under construction or destruction, the function called is the final overrider
in the constructor’s or destructor’s class and not one overriding it in a more-derived class. If the virtual
function call uses an explicit class member access (5.2.5) and the object expression refers to the complete
object of x or one of that object’s base class subobjects but not x or one of its base class subobjects, the
behavior is undefined.
As has already been mentioned there is no "safe way". In fact as has been pointed out by PcAF the lifetime of A has already ended by the time you reach B's destructor.
I also just want to point out that this is actually a good thing! There has to be a strict order in which objects get destroyed.
Now what you should do is tell B beforehand that A is about to get destructed.
It is as simple as
void ~A( void ) {
b->detach_from_me_i_am_about_to_get_destructed( this );
}
Passing the this pointer might be necessary or not depending on the design ob B (If B holds many references, it might need to know which one to detach. If it only holds one, the this pointer is superfluous).
Just make sure that appropriate member functions are private, so that the interface only can be used in the intended way.
Remark:
This is a simple light-weight solution that is fine if you yourself completely control the communication between A and B. Do not under any circumstances design this to be a network protocol! That will require a lot more safety fences.
Consider this:
struct b
{
b()
{
cout << "b()" << endl;
}
~b()
{
cout << "~b()" << endl;
}
};
struct a
{
b ob;
a()
{
cout << "a()" << endl;
}
~a()
{
cout << "~a()" << endl;
}
};
int main()
{
a oa;
}
//Output:
b()
a()
~a()
~b()
"Then the destructor of A will invoke the destructor of B, since it owns it." This is not the correct way of invocation of destructors in case of composite objects. If you see above example then, first a gets destroyed and then b gets destroyed. a's destructor won't invoke b's destructor so that the control would return back to a's destructor.
"What will be a safe way to access A in the destructor of B?". As per above example a is already destroyed therefore a cannot be accessed in b's destructor.
"since we may also be in the destructor of A).". This is not correct. Again, when the control goes out of a's destructor then only control enters b's destructor.
Destructor is a member-function of a class T. Once the control goes out of destructor, the class T cannot be accessed. All the data-members of class T can be accessed in class T's constructors and destructor as per above example.
If you look only on the relations of the two classes A and B, the construction is well:
class A {
B son;
A(): B(this) {}
};
class B {
A* parent;
B(A* myparent): parent(myparent) {}
~B() {
// do not use parent->... because parent's lifetime may be over
parent = NULL; // always safe
}
}
The problems arise, if objects of A and B are proliferated to other program units. Then you should use the tools from std::memory like std::shared_ptr or std:weak_ptr.

C++: what happens when you delete a pointer to an object of a class without explicit destructor?

What happens when you delete a pointer to an object of a class that does not have a declared destructor?
Every class (or struct) has a destructor (unless it is a POD. If you do not declare one the compiler will add an implicit destructor. Take the following class as an example:
struct A
{
std::string test;
};
No destructor was defined for A. Yet it has one, because the compiler automatically adds it. It isn't even empty. It would call the destructor of test, because std::string has a destructor itself.
According to the C++ Standard (12.4 Destructors)
4 If a class has no user-declared destructor, a destructor is
implicitly declared as defaulted (8.4). An implicitlydeclared
destructor is an inline public member of its class.
11... A destructor is also invoked implicitly through use of a delete- expression (5.3.5) for a constructed object allocated by a new-expression (5.3.4);
And all destructors do the following except that implicitly defined destructor has an empty body and consequently does not have automatic objects allocated within its body.
8 After executing the body of the destructor and destroying any
automatic objects allocated within the body, a destructor for class X
calls the destructors for X’s direct non-variant non-static data
members, the destructors for X’s direct base classes and, if X is the
type of the most derived class (12.6.2), its destructor calls the
destructors for X’s virtual base classes. All destructors are called
as if they were referenced with a qualified name, that is, ignoring
any possible virtual overriding destructors in more derived classes.
Bases and members are destroyed in the reverse order of the completion
of their constructor (see 12.6.2). A return statement (6.6.3) in a
destructor might not directly return to the caller; before
transferring control to the caller, the destructors for the members
and bases are called. Destructors for elements of an array are called
in reverse order of their construction (see 12.6).
I assume that by delete a pointer p to a class C you mean
C *p;
<some init and work>
delete p;
If class C does not have a destructor explicitly declared the compiler will add a destructor implicitly.
This implicitly destructor gets called upon destruction of an instance of
the class doing nothing.
n.b. the implicitly added destructor is inline public.
Implicitly-declared destructor
If no user-defined destructor is provided for a class type (struct, class, or union), the compiler will always declare a destructor as an inline public member of its class.
Deleted implicitly-declared destructor
The implicitly-declared or defaulted destructor for class T is undefined (until C++11)defined as deleted (since C++11) if any of the following is true:
T has a non-static data member that cannot be destructed (has deleted or inaccessible destructor)
T has direct or virtual base class that cannot be destructed (has deleted or inaccessible destructors)
T is a union and has a variant member with non-trivial destructor.
(since C++11)
The implicitly-declared destructor is virtual (because the base class has a virtual destructor) and the lookup for the deallocation function (operator delete() results in a call to ambiguous, deleted, or inaccessible function.
#include <iostream>
struct A
{
int i;
A ( int i ) : i ( i ) {}
~A()
{
std::cout << "~a" << i << std::endl;
}
};
int main()
{
A a1(1);
A* p;
{ // nested scope
A a2(2);
p = new A(3);
} // a2 out of scope
delete p; // calls the destructor of a3
}
output:
~a2
~a3
~a1

Destructor's functionalities

For a simple class like this:
class X {
public:
//...
private:
int *ptr;
};
X::~X() {
delete ptr;
}
I have written a destructor to free the memory pointed to by ptr. Now, I am wondering, if my destructor stays like this, when is ptr actually destroyed?
Thanks
I think this article might answer most of your questions
"Destructors are implicitly called when an automatic object (a local
object that has been declared auto or register, or not declared as
static or extern) or temporary object passes out of scope. They are
implicitly called at program termination for constructed external and
static objects. Destructors are invoked when you use the delete
operator for objects created with the new operator."
More specifically:
"The destructors of base classes and members are called in the reverse
order of the completion of their constructor:
The destructor for a class object is called before destructors for
members and bases are called.
Destructors for nonstatic members are called before destructors for
base classes are called.
Destructors for nonvirtual base classes are called before destructors
for virtual base classes are called."
delete invokes the destructor of the object that is being deleted and then frees the memory it occupied.
The destructor will calls in the end of scope where the specific instance are.
The local version of ptr for a given instance of X will be destroyed when the object goes out of scope. For example:
int main()
{
X obj;
for (int i=0; i<10; i++) {
X tmp;
// do work with tmp
...
// tmp goes out of scope here
// the destructor tmp::~X will be called here, immediately after each iteration ends
}
// obj goes out of scope here
// the destructor obj::~X called when the program ends
}

Base class's destructor called without destroying the base class!

#include<iostream>
using namespace std;
class A
{
public:
int i;
A() {cout<<"A()"<<endl;}
~A() {cout<<"~A()"<<endl;}
};
class B:public A
{
public:
int j;
B(): j(10)
{
this->i=20;
this->~A();
}
};
int main()
{
B abc;
cout<<"i="<<abc.i<<" j="<<abc.j<<endl;
}//main
Two questions:
How come A's destructor gets called like an ordinary function instead of destroying the object? (or is it some kind of rule that the base class will be destroyed only if the child class's destructor calls the base class's destructor?) I was trying out this sample code to find out how the destructor works. So if simply calling the destructor function does not destruct the object, then there is obviously some other kind of call that calls the destructor and only then the object is destructed. What's so special in that kind of call and what call is it?
Is there a way to have an initialization list for A in B's constructor? Something like this:
class B:public A
{
B(): j(10), A():i(20) {}
};
Destructor is like any other normal function which you can call (but you should never do it unless you use a placement new). When you call delete on a object two things happen: Destructor is called for cleanup and then operator delete is called to release the memory allocated for the object. Here the second step is not happening.
No, you can not call it like that. What you can do is some thing like this:
class A
{
public:
A(int n) : i(n){}
};
class B : public A
{
public:
B() : A(20), j(10){}
};
The base class's destructor should be virtual. Here, as it's created on the stack, it's not problem, but anyway..
No, but you can call the class A() constructor in the initialize list of B's constructor, like this:
B(): A( .. ), ...
A* a = new B();
//..
delete a;
will not call B's destructor unless class A destructor is virtual. That's why STL containers should not be derived - theirs destructors are not virtual.
#Nav: no, your understanding of "destroyed" is just wrong. When an object's destructor is called, the object is destroyed. You seem to believe that the memory it resided in evaporates entirely, but that never happens. The object no longer exists, but some garbage data is typically left over by the object, and if you're willing to break the rules of C++ and invoke undefined behavior, then you can read those leftover bytes, and they'll look like the object, and because there are no runtime checks on whether you're accessing a valid object, you can often treat them as an object. Which you do.
It's illegal, it's undefined behavior, but in practice it often works.
Once again, a destructor does not physically vaporize the memory. Your RAM still has the same capacity after a destructor has executed. Conceptually, the object no longer exists once the destructor has run. But the data it contained is still there in memory.
For point:
This is an undefined behaviour but only ~A() is called though an instance of class B because ~A() is not declared virtual. See more on Wikipedia.
No. For derived classes, first call your parent class, then assign parameters.
For point 1) on Wikipedia:
having no virtual destructor, while
deleting an instance of class B will
correctly call destructors for both B
and A if the object is deleted as an
instance of B, an instance of B
deleted via a pointer to its base
class A will produce undefined
behaviour.
Example (for point 2):
B(): A(), j(10) {}
or
B(): A() {j = 10;}
1) Destructor calling order in C++ is reverse order of the constructor calling order. So first derived class object get destroy and then base class object.
2) No.
In the code that you are giving, you are indeed destroying the base class and as such i. Calling a destructor and then using the dead object is undefined behavior - it may work or it may crash.
Should i was something that is more complex that an int (for example a vector), trying to do anything with that would probably result in a crash.
If you call ~SomeClass() yourself, explicitly, the destructor function will be called. Which leaves the object (in this case, the base class part of the object) in a destroyed state.
Since the destructor is not virtual, the destructor of derived classes will not be called, but base classes of SomeClass will be destroyed too.
Trying to find out if A is really destroyed by just using the i member, is not a good test. In fact, you can't test this, since using the object results in undefined behavour. It may work, or it may not (in your case, it probably will print "i=20 j=10", but i is already destroyed).