Is it legal C++ to create a worker-object on the stack in the destructor of some master-object and pass the this
pointer of the master-object to the helper-object? The helper-object would then also call member functions of the master-object or access member-variables.
In other words, is the following legal C++?
struct MasterClass
{
MasterClass (int data);
~MasterClass ();
int data;
};
struct WorkerClass
{
WorkerClass (MasterClass *m) : m (m) { }
void do_some_work () { m->data = 42; }
MasterClass *m;
};
MasterClass::MasterClass (int data)
: data (data)
{ }
MasterClass::~MasterClass ()
{
WorkerClass w (this);
w.do_some_work ();
}
int main ()
{
MasterClass m (7);
}
I understand that the lifetime of the master-object ends once the destructor begins to execute. But I believe it is legal to call non-virtual member functions in the destructor of any object, which make use of the implicit this
argument/parameter.
In one word: YES.
A destructor takes no arguments and has no return type. Its address cannot be taken. Destructors cannot be declared const , volatile , const volatile or static . A destructor can be declared virtual or pure virtual .
It destroys the object that's pointed to (using its destructor, if it has one, and doing nothing otherwise), and deallocates the memory that's pointed to. You must previously have used new to allocate the memory and create the object there.
Destructors can freely call class member functions and access class member data.
Yes and no.
Yes, because its legal in this very short example you've shown.
No, because it might result in UB, there are some caveats surrounding usage of an object during destruction
TLDR It's always fine if you don't have any inheritance.
Now, for the cases where it is not fine to use an object during destruction.
The following cases will assume the following is already written
struct V;
struct A;
struct B;
struct D;
void foo(A* a = nullptr);
struct V {
virtual void f();
virtual void g();
};
struct A : virtual V {
virtual void f();
};
struct B : virtual V {
virtual void g();
~B() {
foo();
}
};
struct D : A, B {
virtual void f();
virtual void g();
~D() {
foo(this);
}
};
int main() {
D d;
}
Upon the destruction of x
(aka as soon as its destructor is called)
If the virtual function call uses an explicit class member access and the object expression refers to the complete object of
x
or one of that object's base class subobjects but notx
or one of its base class subobjects, the behavior is undefined.
Which means, if you use a explicit class member access to call a virtual function with a pointer pointing to the entirety of x
, but somehow the pointer isn't the type of x
nor its bases, the behaviour is undefined.
void foo(A* a) {
static auto ptr = a;
ptr->g(); // UB when called from ~B
// ptr refers to B, but is neither B nor its base
}
typeid
If the operand of
typeid
refers to the object under construction or destruction and the static type of the operand is neither the constructor or destructor's class nor one of its bases, the behavior is undefined.
Likewise, if the operand refers to the object being destructed, yet somehow isn't the object and its bases, the behaviour is undefined.
void foo(A* a) {
static auto ptr = a;
typeid(*ptr); // UB when called from ~B()
// ptr refers to B, but is neither B nor its base
}
dynamic_cast
If the operand of the
dynamic_cast
refers to the object under construction or destruction and the static type of the operand is not a pointer to or object of the constructor or destructor's own class or one of its bases, thedynamic_cast
results in undefined behavior.
Same deal.
void foo(A* a) {
static auto ptr = a;
dynamic_cast<B*>(ptr); // UB when called from ~B()
// ptr refers to B, but is neither B nor its base
}
Now, if you think this is a fiasco and didn't understand what is going on, just don't pass this
anywhere in a destructor.
All quotes from http://eel.is/c++draft/class.cdtor
Yes, this is legal, since the master object will not be destroyed before the termination of execution of the destructor.
However, this is not a good practice in general.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With