Передача указателя "this" другому классу / функции в деструкторе
Является ли законным C++ для создания рабочего объекта в стеке в деструкторе некоторого главного объекта и передачи this
указатель мастер-объекта на вспомогательный объект? Затем вспомогательный объект будет также вызывать функции-члены главного объекта или обращаться к переменным-членам.
Другими словами, является ли следующий законный 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);
}
Я понимаю, что время жизни мастер-объекта заканчивается, как только деструктор начинает выполняться. Но я считаю, что законно вызывать не виртуальные функции-члены в деструкторе любого объекта, который использует неявный this
Аргумент / параметр.
2 ответа
И да и нет.
Да, потому что это законно в этом очень коротком примере, который вы показали.
Нет, потому что это может привести к UB, есть некоторые предостережения, связанные с использованием объекта во время уничтожения
TLDR Всегда хорошо, если у тебя нет наследства.
Теперь для случаев, когда использование объекта во время уничтожения не подходит.
В следующих случаях предполагается, что следующее уже написано
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;
}
Вызов виртуальных функций
После уничтожения x
(иначе, как только его деструктор называется)
Если вызов виртуальной функции использует явный доступ к члену класса, а выражение объекта ссылается на полный объект
x
или один из подобъектов базового класса этого объекта, но неx
или один из его подобъектов базового класса, поведение не определено.
Это означает, что если вы используете явный доступ к члену класса для вызова виртуальной функции с указателем, указывающим на всю x
, но почему-то указатель не тип x
ни его основы, поведение не определено.
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
Если операнд
typeid
относится к строящемуся или разрушаемому объекту, а статический тип операнда не является ни классом конструктора или деструктора, ни одной из его основ, поведение не определено.
Аналогично, если операнд относится к разрушаемому объекту, но каким-то образом не является объектом и его основами, поведение не определено.
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
Если операнд
dynamic_cast
относится к строящемуся или разрушаемому объекту, а статический тип операнда не является указателем или объектом собственного класса конструктора или деструктора или одной из его баз,dynamic_cast
приводит к неопределенному поведению.
Та же сделка
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
}
Заключение
Теперь, если вы думаете, что это фиаско и не поняли, что происходит, просто не проходите мимо this
где-нибудь в деструкторе.
Все цитаты из http://eel.is/c++draft/class.cdtor
Да, это законно, поскольку главный объект не будет уничтожен до прекращения выполнения деструктора.
Тем не менее, это не очень хорошая практика в целом.