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.
Related
This question already has answers here:
Automatic destruction of object even after calling destructor explicitly
(5 answers)
Closed 3 years ago.
This code:
#include <iostream>
class Base { };
class Derived : public Base
{
public:
~Derived()
{
std::cout<< "Derived dtor" << std::endl;
}
};
int main()
{
Derived objD;
objD.~Derived();
return 0;
}
prints:
Derived dtor // destructor called
Derived dtor // printed by 'return 0'
I don't have idea from where comes the second line.
With this main:
int main()
{
Derived objD;
return 0;
}
It prints only one line.
What you have here is undefined behavior as per the C++ standard:
As per [class.dtor]/16
Once a destructor is invoked for an object, the object no longer exists; the behavior is undefined if the destructor is invoked for an object whose lifetime has ended (6.8). [ Example: If the destructor for an automatic object is explicitly invoked, and the block is subsequently left in a manner that would ordinarily invoke implicit destruction of the object, the behavior is undefined. —end example ]
You explicitly call the destructor for the automatic object objD with this statement:
objD.~Derived();
And the implicit destruction of the object is invoked at the end of its scope the closing }.
You create an object on the stack. This object has automatic lifetime managed by its surrounding scope. If you manually invoke the destructor, you do what the compiler does for you, and you end up with undefined behavior.
you call the destructor, c++ have an automated call for destructor after the main function, or after the object is not in used and then the destructor call. you do double "free"
Object destructors are always called when the object goes out of scope. That's a fundamental part of how C++ is designed, enabling memory-safe exceptions, RAII, etc. Manually calling the destructor first has no bearing on that, so if you call it yourself it will (most likely, this is UB) run twice as you can see.
Calling a destructor manually is almost always incorrect and will result in Undefined Behaviour. The one case where it's allowed is when you've created an object in separately-allocated memory via "placement new".
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
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
}
When is a non-pointer member of a class destructed? Example:
class foo {
private:
int a;
public:
foo(int sa):a(sa){}
~foo(){}//does anything need to be done here?
};
{
foo(10);
}//the destructor is called
Should anything be done inside the destructor? Thanks!
No, not a thing. a will be destroyed after any code in your destructor completes. In a case like this, you don't even need to declare a destructor; the compiler will do the right thing on its own.
An object's contents are destroyed in inverse order of their appearance in the class definition after the execution of the object's destructor.
A non-pointer member of an object is destructed after the containing object's destructor has completed.
#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).